@@ -2577,6 +2577,9 @@ void TPDisk::ProcessFastOperationsQueue() {
2577
2577
case ERequestType::RequestMarkDirty:
2578
2578
ProcessMarkDirty (static_cast <TMarkDirty&>(*req));
2579
2579
break ;
2580
+ case ERequestType::RequestChunkShredResult:
2581
+ ProcessChunkShredResult (static_cast <TChunkShredResult&>(*req));
2582
+ break ;
2580
2583
default :
2581
2584
Y_FAIL_S (" Unexpected request type# " << TypeName (*req));
2582
2585
break ;
@@ -3187,6 +3190,7 @@ bool TPDisk::PreprocessRequest(TRequestBase *request) {
3187
3190
case ERequestType::RequestPreShredCompactVDiskResult:
3188
3191
case ERequestType::RequestShredVDiskResult:
3189
3192
case ERequestType::RequestMarkDirty:
3193
+ case ERequestType::RequestChunkShredResult:
3190
3194
break ;
3191
3195
case ERequestType::RequestStopDevice:
3192
3196
BlockDevice->Stop ();
@@ -3883,6 +3887,7 @@ bool TPDisk::HandleReadOnlyIfWrite(TRequestBase *request) {
3883
3887
case ERequestType::RequestPreShredCompactVDiskResult:
3884
3888
case ERequestType::RequestShredVDiskResult:
3885
3889
case ERequestType::RequestMarkDirty:
3890
+ case ERequestType::RequestChunkShredResult:
3886
3891
// These requests don't require response.
3887
3892
return true ;
3888
3893
}
@@ -3913,8 +3918,31 @@ void TPDisk::AddCbsSet(ui32 ownerId) {
3913
3918
SchedulerConfigure (conf);
3914
3919
}
3915
3920
3921
+ TChunkIdx TPDisk::GetUnshreddedFreeChunk () {
3922
+ // Find a free unshredded chunk
3923
+ for (TFreeChunks* freeChunks : {&Keeper.UntrimmedFreeChunks , &Keeper.TrimmedFreeChunks }) {
3924
+ for (auto it = freeChunks->begin (); it != freeChunks->end (); ++it) {
3925
+ TChunkIdx chunkIdx = *it;
3926
+ TChunkState& state = ChunkState[chunkIdx];
3927
+ // Look for free chunks that haven't been shredded in this generation
3928
+ if (state.CommitState == TChunkState::FREE && state.IsDirty && state.ShredGeneration < ShredGeneration) {
3929
+ // Found an unshredded free chunk
3930
+ TChunkIdx unshreddedChunkIdx = freeChunks->PopAt (it);
3931
+ Y_VERIFY (unshreddedChunkIdx == chunkIdx);
3932
+ // Mark it as being shredded and update its generation
3933
+ LOG_DEBUG_S (*PCtx->ActorSystem , NKikimrServices::BS_PDISK_SHRED,
3934
+ " PDisk# " << PCtx->PDiskId
3935
+ << " found unshredded free chunk# " << chunkIdx
3936
+ << " ShredGeneration# " << ShredGeneration);
3937
+ return unshreddedChunkIdx;
3938
+ }
3939
+ }
3940
+ }
3941
+ return 0 ;
3942
+ }
3943
+
3916
3944
void TPDisk::ProgressShredState () {
3917
- LOG_DEBUG_S (*PCtx->ActorSystem , NKikimrServices::BS_PDISK_SHRED,
3945
+ LOG_TRACE_S (*PCtx->ActorSystem , NKikimrServices::BS_PDISK_SHRED,
3918
3946
" ProgressShredState at PDisk# " << PCtx->PDiskId
3919
3947
<< " ShredGeneration# " << ShredGeneration
3920
3948
<< " ShredState# " << (ui32)ShredState);
@@ -3959,9 +3987,80 @@ void TPDisk::ProgressShredState() {
3959
3987
<< " has finished all pre-shred compact VDisk requests"
3960
3988
<< " ShredGeneration# " << ShredGeneration
3961
3989
<< " finishedCount# " << finishedCount);
3990
+ // All preparations are done, no junk chunks can be unmarked,
3991
+ // Update chunk states and start shredding the empty space
3992
+ for (TChunkIdx chunkIdx = 0 ; chunkIdx < ChunkState.size (); ++chunkIdx) {
3993
+ TChunkState& state = ChunkState[chunkIdx];
3994
+ // Update shred generation for all the clean chunks
3995
+ if (!state.IsDirty ) {
3996
+ state.ShredGeneration = ShredGeneration;
3997
+ }
3998
+ }
3962
3999
ShredState = EShredStateSendShredVDisk;
3963
4000
}
3964
4001
if (ShredState == EShredStateSendShredVDisk) {
4002
+ // Shred free space while possible
4003
+ if (ChunkBeingShredded == 0 ) {
4004
+ ChunkBeingShredded = GetUnshreddedFreeChunk ();
4005
+ }
4006
+ if (ChunkBeingShredded != 0 ) {
4007
+ // Continue shredding the free chunk
4008
+ while (true ) {
4009
+ if (ChunkBeingShreddedInFlight >= 2 ) {
4010
+ // We have enough in-flight requests, don't start a new one
4011
+ return ;
4012
+ }
4013
+ if (ChunkBeingShreddedNextSectorIdx * Format.SectorSize >= Format.ChunkSize ) {
4014
+ ++ChunkBeingShreddedIteration;
4015
+ ChunkBeingShreddedNextSectorIdx = 0 ;
4016
+ }
4017
+ if (ChunkBeingShreddedIteration >= 2 ) {
4018
+ // We have enough iterations, don't start a new one, just wait for the in-flight requests to finish
4019
+ if (ChunkBeingShreddedInFlight > 0 ) {
4020
+ return ;
4021
+ }
4022
+ // Done shredding the chunk, mark it clean and push it back to the free chunks
4023
+ LOG_DEBUG_S (*PCtx->ActorSystem , NKikimrServices::BS_PDISK_SHRED, " PDisk# " << PCtx->PDiskId
4024
+ << " is done shredding chunk ChunkBeingShredded# " << ChunkBeingShredded);
4025
+ TChunkState &state = ChunkState[ChunkBeingShredded];
4026
+ state.OperationsInProgress --;
4027
+ state.IsDirty = false ;
4028
+ state.ShredGeneration = ShredGeneration;
4029
+ Y_VERIFY (ChunkState[ChunkBeingShredded].OperationsInProgress == 0 );
4030
+ Keeper.UntrimmedFreeChunks .PushFront (ChunkBeingShredded);
4031
+ ChunkBeingShredded = GetUnshreddedFreeChunk ();
4032
+ ChunkBeingShreddedIteration = 0 ;
4033
+ ChunkBeingShreddedNextSectorIdx = 0 ;
4034
+ }
4035
+ if (ChunkBeingShredded) {
4036
+ if (ChunkBeingShreddedIteration == 0 && ChunkBeingShreddedNextSectorIdx == 0 ) {
4037
+ Y_VERIFY (ChunkState[ChunkBeingShredded].OperationsInProgress == 0 );
4038
+ ChunkState[ChunkBeingShredded].OperationsInProgress ++;
4039
+ }
4040
+ // Continue shredding the chunk: send a write request to the device using the iteration-specific pattern
4041
+ THolder<TAlignedData>& payload = ShredPayload[ChunkBeingShreddedIteration];
4042
+ if (payload == nullptr ) {
4043
+ payload = MakeHolder<TAlignedData>(Format.RoundUpToSectorSize (2097152 ));
4044
+ ui8* data = payload->Get ();
4045
+ memset (data, ChunkBeingShreddedIteration == 0 ? 0x55 : 0xaa , payload->Size ());
4046
+ }
4047
+ ui64 size = std::min ((ui64)Format.ChunkSize - ChunkBeingShreddedNextSectorIdx * Format.SectorSize , (ui64)payload->Size ());
4048
+ ui64 offset = Format.Offset (ChunkBeingShredded, ChunkBeingShreddedNextSectorIdx);
4049
+ ui64 reqIdx = ShredReqIdx++;
4050
+ TCompletionAction *completionAction = new TChunkShredCompletion (this , ChunkBeingShredded, ChunkBeingShreddedNextSectorIdx, size, TReqId (TReqId::ChunkShred, reqIdx));
4051
+ ++ChunkBeingShreddedInFlight;
4052
+ ChunkBeingShreddedNextSectorIdx += size / Format.SectorSize ;
4053
+ Mon.ChunkShred .CountRequest (size);
4054
+ BlockDevice->PwriteAsync (payload->Get (), size, offset, completionAction,
4055
+ TReqId (TReqId::ChunkShred, reqIdx), {});
4056
+ return ;
4057
+ }
4058
+ break ;
4059
+ }
4060
+ }
4061
+
4062
+ // If there are no free chunks unshredded, we should ask a vdisk to shred its free space
4063
+ ui32 shreddedFreeChunks = Keeper.GetFreeChunkCount ();
3965
4064
ui32 finishedCount = 0 ;
3966
4065
for (ui32 ownerId = 0 ; ownerId < OwnerData.size (); ++ownerId) {
3967
4066
TOwnerData &data = OwnerData[ownerId];
@@ -3971,22 +4070,31 @@ void TPDisk::ProgressShredState() {
3971
4070
} else if (data.ShredState != TOwnerData::VDISK_SHRED_STATE_SHRED_REQUESTED
3972
4071
&& data.ShredState != TOwnerData::VDISK_SHRED_STATE_SHRED_FINISHED) {
3973
4072
std::vector<TChunkIdx> chunksToShred;
3974
- chunksToShred.reserve (ChunkState. size () );
4073
+ chunksToShred.reserve (shreddedFreeChunks/ 2 );
3975
4074
for (TChunkIdx chunkIdx = 0 ; chunkIdx < ChunkState.size (); ++chunkIdx) {
3976
- if (ChunkState[chunkIdx].OwnerId == ownerId) {
3977
- // TODO(cthulhu): check if chunk is dirty
4075
+ TChunkState& state = ChunkState[chunkIdx];
4076
+ // We need to shred only chunks that got dirty before the current shred generation
4077
+ if (state.OwnerId == ownerId && state.IsDirty && state.ShredGeneration < ShredGeneration) {
3978
4078
chunksToShred.push_back (chunkIdx);
4079
+ if (chunksToShred.size () >= shreddedFreeChunks/2 ) {
4080
+ break ;
4081
+ }
3979
4082
}
3980
4083
}
3981
- THolder<TEvShredVDisk> shredRequest (new TEvShredVDisk (ShredGeneration, chunksToShred));
3982
- LOG_DEBUG_S (*PCtx->ActorSystem , NKikimrServices::BS_PDISK_SHRED,
3983
- " PDisk# " << PCtx->PDiskId
3984
- << " sends shred request to VDisk# " << data.VDiskId
3985
- << " ownerId# " << ownerId
3986
- << " request# " << shredRequest->ToString ());
3987
- PCtx->ActorSystem ->Send (new IEventHandle (data.CutLogId , PCtx->PDiskActor , shredRequest.Release ()));
3988
- data.ShredState = TOwnerData::VDISK_SHRED_STATE_SHRED_REQUESTED;
3989
- data.LastShredGeneration = ShredGeneration;
4084
+ if (chunksToShred.size () > 0 ) {
4085
+ THolder<TEvShredVDisk> shredRequest (new TEvShredVDisk (ShredGeneration, chunksToShred));
4086
+ LOG_DEBUG_S (*PCtx->ActorSystem , NKikimrServices::BS_PDISK_SHRED,
4087
+ " PDisk# " << PCtx->PDiskId
4088
+ << " sends shred request to VDisk# " << data.VDiskId
4089
+ << " ownerId# " << ownerId
4090
+ << " request# " << shredRequest->ToString ());
4091
+ PCtx->ActorSystem ->Send (new IEventHandle (data.CutLogId , PCtx->PDiskActor , shredRequest.Release ()));
4092
+ data.ShredState = TOwnerData::VDISK_SHRED_STATE_SHRED_REQUESTED;
4093
+ data.LastShredGeneration = ShredGeneration;
4094
+ } else {
4095
+ data.ShredState = TOwnerData::VDISK_SHRED_STATE_SHRED_FINISHED;
4096
+ data.LastShredGeneration = ShredGeneration;
4097
+ }
3990
4098
}
3991
4099
if (data.ShredState != TOwnerData::VDISK_SHRED_STATE_SHRED_FINISHED) {
3992
4100
LOG_DEBUG_S (*PCtx->ActorSystem , NKikimrServices::BS_PDISK_SHRED,
@@ -4170,12 +4278,52 @@ void TPDisk::ProcessShredVDiskResult(TShredVDiskResult& request) {
4170
4278
ShredRequesters.clear ();
4171
4279
return ;
4172
4280
}
4173
- OwnerData[request.Owner ].ShredState = TOwnerData::VDISK_SHRED_STATE_SHRED_FINISHED ;
4281
+ OwnerData[request.Owner ].ShredState = TOwnerData::VDISK_SHRED_STATE_COMPACT_FINISHED ;
4174
4282
ProgressShredState ();
4175
4283
}
4176
4284
4177
4285
void TPDisk::ProcessMarkDirty (TMarkDirty& request) {
4178
- Y_UNUSED (request);
4286
+ LOG_DEBUG_S (*PCtx->ActorSystem , NKikimrServices::BS_PDISK_SHRED,
4287
+ " ProcessMarkDirty at PDisk# " << PCtx->PDiskId
4288
+ << " ShredGeneration# " << ShredGeneration
4289
+ << " request# " << request.ToString ());
4290
+ {
4291
+ bool isLogged = false ;
4292
+ ui64 markedDirty = 0 ;
4293
+ TGuard<TMutex> guard (StateMutex);
4294
+ for (auto chunkIdx : request.ChunksToMarkDirty ) {
4295
+ if (chunkIdx >= ChunkState.size ()) {
4296
+ if (!isLogged) {
4297
+ isLogged = true ;
4298
+ LOG_CRIT_S (*PCtx->ActorSystem , NKikimrServices::BS_PDISK_SHRED,
4299
+ " MarkDirty contains invalid chunkIdx# " << chunkIdx << " for PDisk# " << PCtx->PDiskId
4300
+ << " ShredGeneration# " << ShredGeneration << " request# " << request.ToString ());
4301
+ }
4302
+ } else {
4303
+ if (!ChunkState[chunkIdx].IsDirty ) {
4304
+ ChunkState[chunkIdx].IsDirty = true ;
4305
+ markedDirty++;
4306
+ LOG_DEBUG_S (*PCtx->ActorSystem , NKikimrServices::BS_PDISK_SHRED,
4307
+ " PDisk# " << PCtx->PDiskId << " marked chunkIdx# " << chunkIdx << " as dirty"
4308
+ << " chunk.ShredGeneration# " << ChunkState[chunkIdx].ShredGeneration
4309
+ << " ShredGeneration# " << ShredGeneration);
4310
+ }
4311
+ }
4312
+ }
4313
+ if (markedDirty > 0 ) {
4314
+ // TODO(cthulhu): save dirty chunks to syslog
4315
+ }
4316
+ }
4317
+ }
4318
+
4319
+ void TPDisk::ProcessChunkShredResult (TChunkShredResult& request) {
4320
+ LOG_TRACE_S (*PCtx->ActorSystem , NKikimrServices::BS_PDISK_SHRED,
4321
+ " ProcessChunkShredResult at PDisk# " << PCtx->PDiskId
4322
+ << " ShredGeneration# " << ShredGeneration
4323
+ << " request# " << request.ToString ());
4324
+ Y_ABORT_UNLESS (ChunkBeingShreddedInFlight > 0 );
4325
+ --ChunkBeingShreddedInFlight;
4326
+ ProgressShredState ();
4179
4327
}
4180
4328
4181
4329
// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0 commit comments