/// <inheritdoc /> public override SystemState NextState(SystemState start) { var result = SystemState.Copy(ref start); switch (start.Phase) { case Phase.REST: result.Phase = Phase.PREP_INDEX_CHECKPOINT; break; case Phase.PREP_INDEX_CHECKPOINT: result.Phase = Phase.WAIT_INDEX_ONLY_CHECKPOINT; break; case Phase.WAIT_INDEX_ONLY_CHECKPOINT: result.Phase = Phase.REST; break; default: throw new FasterException("Invalid Enum Argument"); } return(result); }
/// <inheritdoc /> public override SystemState NextState(SystemState start) { var nextState = SystemState.Copy(ref start); switch (start.phase) { case Phase.REST: nextState.phase = Phase.PREPARE_GROW; break; case Phase.PREPARE_GROW: nextState.phase = Phase.IN_PROGRESS_GROW; break; case Phase.IN_PROGRESS_GROW: nextState.phase = Phase.REST; break; default: throw new FasterException("Invalid Enum Argument"); } return(nextState); }
private void HandleCheckpointingPhases() { var previousState = SystemState.Make(threadCtx.phase, threadCtx.version); var finalState = SystemState.Copy(ref _systemState); // Don't play around when system state is being changed if (finalState.phase == Phase.INTERMEDIATE) { return; } // We need to move from previousState to finalState one step at a time do { var currentState = default(SystemState); if (previousState.word == finalState.word) { currentState.word = previousState.word; } else { currentState = GetNextState(previousState, _checkpointType); } switch (currentState.phase) { case Phase.PREP_INDEX_CHECKPOINT: { if (!threadCtx.markers[EpochPhaseIdx.PrepareForIndexCheckpt]) { if (epoch.MarkAndCheckIsComplete(EpochPhaseIdx.PrepareForIndexCheckpt, threadCtx.version)) { GlobalMoveToNextCheckpointState(currentState); } threadCtx.markers[EpochPhaseIdx.PrepareForIndexCheckpt] = true; } break; } case Phase.INDEX_CHECKPOINT: { if (_checkpointType == CheckpointType.INDEX_ONLY) { // Reseting the marker for a potential FULL or INDEX_ONLY checkpoint in the future threadCtx.markers[EpochPhaseIdx.PrepareForIndexCheckpt] = false; } if (IsIndexFuzzyCheckpointCompleted()) { GlobalMoveToNextCheckpointState(currentState); } break; } case Phase.PREPARE: { if (!threadCtx.markers[EpochPhaseIdx.Prepare]) { // Thread local action AcquireSharedLatchesForAllPendingRequests(); var idx = Interlocked.Increment(ref _hybridLogCheckpoint.info.numThreads); idx -= 1; _hybridLogCheckpoint.info.guids[idx] = threadCtx.guid; if (epoch.MarkAndCheckIsComplete(EpochPhaseIdx.Prepare, threadCtx.version)) { GlobalMoveToNextCheckpointState(currentState); } threadCtx.markers[EpochPhaseIdx.Prepare] = true; } break; } case Phase.IN_PROGRESS: { // Need to be very careful here as threadCtx is changing FasterExecutionContext ctx; if (previousState.phase == Phase.PREPARE) { ctx = threadCtx; } else { ctx = prevThreadCtx; } if (!ctx.markers[EpochPhaseIdx.InProgress]) { prevThreadCtx = threadCtx; InitLocalContext(ref threadCtx, prevThreadCtx.guid); if (epoch.MarkAndCheckIsComplete(EpochPhaseIdx.InProgress, ctx.version)) { GlobalMoveToNextCheckpointState(currentState); } prevThreadCtx.markers[EpochPhaseIdx.InProgress] = true; } break; } case Phase.WAIT_PENDING: { if (!prevThreadCtx.markers[EpochPhaseIdx.WaitPending]) { var notify = (prevThreadCtx.ioPendingRequests.Count == 0); notify = notify && (prevThreadCtx.retryRequests.Count == 0); if (notify) { if (epoch.MarkAndCheckIsComplete(EpochPhaseIdx.WaitPending, threadCtx.version)) { GlobalMoveToNextCheckpointState(currentState); } prevThreadCtx.markers[EpochPhaseIdx.WaitPending] = true; } } break; } case Phase.WAIT_FLUSH: { if (!prevThreadCtx.markers[EpochPhaseIdx.WaitFlush]) { var notify = false; if (FoldOverSnapshot) { notify = (hlog.FlushedUntilAddress >= _hybridLogCheckpoint.info.finalLogicalAddress); } else { notify = (_hybridLogCheckpoint.flushed != null) && _hybridLogCheckpoint.flushed.IsSet; } if (_checkpointType == CheckpointType.FULL) { notify = notify && IsIndexFuzzyCheckpointCompleted(); } if (notify) { WriteHybridLogContextInfo(); if (epoch.MarkAndCheckIsComplete(EpochPhaseIdx.WaitFlush, prevThreadCtx.version)) { GlobalMoveToNextCheckpointState(currentState); } prevThreadCtx.markers[EpochPhaseIdx.WaitFlush] = true; } } break; } case Phase.PERSISTENCE_CALLBACK: { if (!prevThreadCtx.markers[EpochPhaseIdx.CheckpointCompletionCallback]) { // Thread local action functions.CheckpointCompletionCallback(threadCtx.guid, prevThreadCtx.serialNum); if (epoch.MarkAndCheckIsComplete(EpochPhaseIdx.CheckpointCompletionCallback, prevThreadCtx.version)) { GlobalMoveToNextCheckpointState(currentState); } prevThreadCtx.markers[EpochPhaseIdx.CheckpointCompletionCallback] = true; } break; } case Phase.REST: { break; } default: Debug.WriteLine("Error!"); break; } // update thread local variables threadCtx.phase = currentState.phase; threadCtx.version = currentState.version; previousState.word = currentState.word; } while (previousState.word != finalState.word); }
private async ValueTask HandleCheckpointingPhasesAsync(FasterExecutionContext ctx, ClientSession <Key, Value, Input, Output, Context, Functions> clientSession, bool async = true, CancellationToken token = default) { if (async) { clientSession?.UnsafeResumeThread(); } var finalState = SystemState.Copy(ref _systemState); while (finalState.phase == Phase.INTERMEDIATE) { finalState = SystemState.Copy(ref _systemState); } var previousState = ctx != null?SystemState.Make(ctx.phase, ctx.version) : finalState; // We need to move from previousState to finalState one step at a time var currentState = previousState; SystemState startState = GetStartState(finalState); if ((currentState.version < startState.version) || (currentState.version == startState.version && currentState.phase < startState.phase)) { // Fast-forward to beginning of current checkpoint cycle currentState = startState; } do { switch (currentState.phase) { case Phase.PREP_INDEX_CHECKPOINT: { if (ctx != null) { if (!ctx.markers[EpochPhaseIdx.PrepareForIndexCheckpt]) { ctx.markers[EpochPhaseIdx.PrepareForIndexCheckpt] = true; } epoch.Mark(EpochPhaseIdx.PrepareForIndexCheckpt, currentState.version); } if (epoch.CheckIsComplete(EpochPhaseIdx.PrepareForIndexCheckpt, currentState.version)) { GlobalMoveToNextCheckpointState(currentState); } break; } case Phase.INDEX_CHECKPOINT: { if (_checkpointType == CheckpointType.INDEX_ONLY && ctx != null) { // Reseting the marker for a potential FULL or INDEX_ONLY checkpoint in the future ctx.markers[EpochPhaseIdx.PrepareForIndexCheckpt] = false; } if (async && !IsIndexFuzzyCheckpointCompleted()) { clientSession?.UnsafeSuspendThread(); await IsIndexFuzzyCheckpointCompletedAsync(token); clientSession?.UnsafeResumeThread(); } GlobalMoveToNextCheckpointState(currentState); break; } case Phase.PREPARE: { if (ctx != null) { if (!ctx.markers[EpochPhaseIdx.Prepare]) { if (!RelaxedCPR) { AcquireSharedLatchesForAllPendingRequests(ctx); } ctx.markers[EpochPhaseIdx.Prepare] = true; } epoch.Mark(EpochPhaseIdx.Prepare, currentState.version); } if (epoch.CheckIsComplete(EpochPhaseIdx.Prepare, currentState.version)) { GlobalMoveToNextCheckpointState(currentState); } break; } case Phase.IN_PROGRESS: { if (ctx != null) { // Need to be very careful here as threadCtx is changing FasterExecutionContext _ctx; if (previousState.phase == Phase.IN_PROGRESS) { _ctx = ctx.prevCtx; } else { _ctx = ctx; } if (!_ctx.markers[EpochPhaseIdx.InProgress]) { AtomicSwitch(ctx, ctx.prevCtx, _ctx.version); InitContext(ctx, ctx.prevCtx.guid, ctx.prevCtx.serialNum); // Has to be prevCtx, not ctx ctx.prevCtx.markers[EpochPhaseIdx.InProgress] = true; } epoch.Mark(EpochPhaseIdx.InProgress, currentState.version); } // Has to be prevCtx, not ctx if (epoch.CheckIsComplete(EpochPhaseIdx.InProgress, currentState.version)) { GlobalMoveToNextCheckpointState(currentState); } break; } case Phase.WAIT_PENDING: { if (ctx != null) { if (!ctx.prevCtx.markers[EpochPhaseIdx.WaitPending]) { var notify = (ctx.prevCtx.ioPendingRequests.Count == 0); notify = notify && (ctx.prevCtx.retryRequests.Count == 0); if (notify) { ctx.prevCtx.markers[EpochPhaseIdx.WaitPending] = true; } else { break; } } epoch.Mark(EpochPhaseIdx.WaitPending, currentState.version); } if (epoch.CheckIsComplete(EpochPhaseIdx.WaitPending, currentState.version)) { GlobalMoveToNextCheckpointState(currentState); } break; } case Phase.WAIT_FLUSH: { if (ctx == null || !ctx.prevCtx.markers[EpochPhaseIdx.WaitFlush]) { bool notify; if (FoldOverSnapshot) { notify = (hlog.FlushedUntilAddress >= _hybridLogCheckpoint.info.finalLogicalAddress); } else { notify = (_hybridLogCheckpoint.flushedSemaphore != null) && _hybridLogCheckpoint.flushedSemaphore.CurrentCount > 0; } if (async && !notify) { Debug.Assert(_hybridLogCheckpoint.flushedSemaphore != null); clientSession?.UnsafeSuspendThread(); await _hybridLogCheckpoint.flushedSemaphore.WaitAsync(token); clientSession?.UnsafeResumeThread(); _hybridLogCheckpoint.flushedSemaphore.Release(); notify = true; } if (_checkpointType == CheckpointType.FULL) { notify = notify && IsIndexFuzzyCheckpointCompleted(); if (async && !notify) { clientSession?.UnsafeSuspendThread(); await IsIndexFuzzyCheckpointCompletedAsync(token); clientSession?.UnsafeResumeThread(); notify = true; } } if (notify) { if (ctx != null) { ctx.prevCtx.markers[EpochPhaseIdx.WaitFlush] = true; } } else { break; } } if (ctx != null) { epoch.Mark(EpochPhaseIdx.WaitFlush, currentState.version); } if (epoch.CheckIsComplete(EpochPhaseIdx.WaitFlush, currentState.version)) { GlobalMoveToNextCheckpointState(currentState); } break; } case Phase.PERSISTENCE_CALLBACK: { if (ctx != null) { if (!ctx.prevCtx.markers[EpochPhaseIdx.CheckpointCompletionCallback]) { if (ctx.prevCtx.serialNum != -1) { var commitPoint = new CommitPoint { UntilSerialNo = ctx.prevCtx.serialNum, ExcludedSerialNos = ctx.prevCtx.excludedSerialNos }; // Thread local action functions.CheckpointCompletionCallback(ctx.guid, commitPoint); if (clientSession != null) { clientSession.LatestCommitPoint = commitPoint; } } ctx.prevCtx.markers[EpochPhaseIdx.CheckpointCompletionCallback] = true; } epoch.Mark(EpochPhaseIdx.CheckpointCompletionCallback, currentState.version); } if (epoch.CheckIsComplete(EpochPhaseIdx.CheckpointCompletionCallback, currentState.version)) { GlobalMoveToNextCheckpointState(currentState); } break; } case Phase.REST: { break; } default: throw new FasterException("Invalid state found during checkpointing"); } if (ctx != null) { // update thread local variables ctx.phase = currentState.phase; ctx.version = currentState.version; } previousState.word = currentState.word; currentState = GetNextState(currentState, _checkpointType); } while (previousState.word != finalState.word); if (async) { clientSession?.UnsafeSuspendThread(); } }