// kick off a prefetch public override async Task RunPrefetchSession(IAsyncEnumerable <TrackedObjectKey> keys) { int maxConcurrency = 500; using SemaphoreSlim prefetchSemaphore = new SemaphoreSlim(maxConcurrency); Guid sessionId = Guid.NewGuid(); this.blobManager.TraceHelper.FasterProgress($"PrefetchSession {sessionId} started (maxConcurrency={maxConcurrency})"); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); long numberIssued = 0; long numberMisses = 0; long numberHits = 0; long lastReport = 0; void ReportProgress(int elapsedMillisecondsThreshold) { if (stopwatch.ElapsedMilliseconds - lastReport >= elapsedMillisecondsThreshold) { this.blobManager.TraceHelper.FasterProgress( $"FasterKV PrefetchSession {sessionId} elapsed={stopwatch.Elapsed.TotalSeconds:F2}s issued={numberIssued} pending={maxConcurrency-prefetchSemaphore.CurrentCount} hits={numberHits} misses={numberMisses}"); lastReport = stopwatch.ElapsedMilliseconds; } } try { // these are disposed after the prefetch thread is done using var prefetchSession = this.CreateASession(); // for each key, issue a prefetch await foreach (TrackedObjectKey key in keys) { // wait for an available prefetch semaphore token while (!await prefetchSemaphore.WaitAsync(50, this.terminationToken)) { prefetchSession.CompletePending(); ReportProgress(1000); } FasterKV.Key k = key; EffectTracker noInput = null; TrackedObject ignoredOutput = null; var status = prefetchSession.Read(ref k, ref noInput, ref ignoredOutput, userContext: prefetchSemaphore, 0); numberIssued++; switch (status) { case Status.NOTFOUND: case Status.OK: // fast path: we hit in the cache and complete the read numberHits++; prefetchSemaphore.Release(); break; case Status.PENDING: // slow path: upon completion numberMisses++; break; case Status.ERROR: this.partition.ErrorHandler.HandleError(nameof(RunPrefetchSession), "FASTER reported ERROR status", null, true, this.partition.ErrorHandler.IsTerminated); break; } this.terminationToken.ThrowIfCancellationRequested(); prefetchSession.CompletePending(); ReportProgress(1000); } ReportProgress(0); this.blobManager.TraceHelper.FasterProgress($"PrefetchSession {sessionId} is waiting for completion"); // all prefetches were issued; now we wait for them all to complete // by acquiring ALL the semaphore tokens for (int i = 0; i < maxConcurrency; i++) { while (!await prefetchSemaphore.WaitAsync(50, this.terminationToken)) { prefetchSession.CompletePending(); ReportProgress(1000); } } ReportProgress(0); this.blobManager.TraceHelper.FasterProgress($"PrefetchSession {sessionId} completed"); } catch (OperationCanceledException) when(this.terminationToken.IsCancellationRequested) { // partition is terminating } catch (Exception e) when(!Utils.IsFatal(e)) { this.partition.ErrorHandler.HandleError(nameof(RunPrefetchSession), "PrefetchSession {sessionId} encountered exception", e, false, this.partition.ErrorHandler.IsTerminated); } }
// create a tracked object on the main session (only one of these is executing at a time) public abstract ValueTask <TrackedObject> CreateAsync(FasterKV.Key key);
public abstract ValueTask ProcessEffectOnTrackedObject(FasterKV.Key k, EffectTracker tracker);
// read a tracked object on the main session and wait for the response (only one of these is executing at a time) public abstract ValueTask <TrackedObject> ReadAsync(FasterKV.Key key, EffectTracker effectTracker);