{"record":{"id":"cd59351085005434","repo":"microsoft/FASTER","slug":"can-compact-only-until-log-safereadonlyaddress","errorCode":null,"errorMessage":"Can compact only until Log.SafeReadOnlyAddress","messagePattern":"Can compact only until Log\\.SafeReadOnlyAddress","errorType":"exception","errorClass":"FasterException","httpStatus":null,"severity":"error","filePath":"cs/src/core/Compaction/FASTERCompaction.cs","lineNumber":40,"sourceCode":"        /// <returns>Address until which compaction was done</returns>\n        internal long Compact<Input, Output, Context, Functions, CompactionFunctions>(Functions functions, CompactionFunctions cf, ref Input input, ref Output output, long untilAddress, CompactionType compactionType, SessionVariableLengthStructSettings<Value, Input> sessionVariableLengthStructSettings = null)\n            where Functions : IFunctions<Key, Value, Input, Output, Context>\n            where CompactionFunctions : ICompactionFunctions<Key, Value>\n        {\n            return compactionType switch\n            {\n                CompactionType.Scan => CompactScan<Input, Output, Context, Functions, CompactionFunctions>(functions, cf, ref input, ref output, untilAddress, sessionVariableLengthStructSettings),\n                CompactionType.Lookup => CompactLookup<Input, Output, Context, Functions, CompactionFunctions>(functions, cf, ref input, ref output, untilAddress, sessionVariableLengthStructSettings),\n                _ => throw new FasterException(\"Invalid compaction type\"),\n            };\n        }\n\n        private long CompactLookup<Input, Output, Context, Functions, CompactionFunctions>(Functions functions, CompactionFunctions cf, ref Input input, ref Output output, long untilAddress, SessionVariableLengthStructSettings<Value, Input> sessionVariableLengthStructSettings)\n            where Functions : IFunctions<Key, Value, Input, Output, Context>\n            where CompactionFunctions : ICompactionFunctions<Key, Value>\n        {\n            if (untilAddress > hlog.SafeReadOnlyAddress)\n                throw new FasterException(\"Can compact only until Log.SafeReadOnlyAddress\");\n\n            var lf = new LogCompactionFunctions<Key, Value, Input, Output, Context, Functions>(functions);\n            using var fhtSession = For(lf).NewSession<LogCompactionFunctions<Key, Value, Input, Output, Context, Functions>>(sessionVariableLengthStructSettings: sessionVariableLengthStructSettings);\n\n            using (var iter1 = Log.Scan(Log.BeginAddress, untilAddress))\n            {\n                long numPending = 0;\n                while (iter1.GetNext(out var recordInfo))\n                {\n                    ref var key = ref iter1.GetKey();\n                    ref var value = ref iter1.GetValue();\n\n                    if (!recordInfo.Tombstone && !cf.IsDeleted(ref key, ref value))\n                    {\n                        var status = fhtSession.CompactionCopyToTail(ref key, ref input, ref value, ref output, iter1.NextAddress);\n                        if (status.IsPending && ++numPending > 256)\n                        {\n                            fhtSession.CompletePending(wait: true);","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/microsoft/FASTER/blob/321d872eabda6a0345c8bd76419f89723ed864ae/cs/src/core/Compaction/FASTERCompaction.cs#L22-L58","documentation":"Lookup-based compaction can only compact records that are no longer mutable, i.e. below Log.SafeReadOnlyAddress. Passing a untilAddress greater than SafeReadOnlyAddress would race with in-flight writes, so FASTER throws.","triggerScenarios":"Calling fht.Compact<...>(..., untilAddress: X, ...) with CompactionType.Lookup where X > hlog.SafeReadOnlyAddress.","commonSituations":"Compacting with untilAddress set to hlog.TailAddress or a computed address while concurrent writes/reads are shifting the read-only boundary; compacting immediately after heavy inserts.","solutions":["Clamp untilAddress to hlog.SafeReadOnlyAddress before calling Compact.","Use Log.ShiftReadOnlyAddress() to advance SafeReadOnlyAddress if appropriate, then compact.","Pause or quiesce writers, or track a known-durable address from a checkpoint to use as untilAddress."],"exampleFix":"// before\nfht.Compact<Input, Output, Context, Functions, CF>(functions, cf, CompactionType.Lookup, ref input, ref output, hlog.TailAddress);\n// after\nlong untilAddress = hlog.SafeReadOnlyAddress;\nfht.Compact<Input, Output, Context, Functions, CF>(functions, cf, CompactionType.Lookup, ref input, ref output, untilAddress);","handlingStrategy":"validation","validationCode":"long untilAddress = Math.Min(requestedUntilAddress, fht.Log.SafeReadOnlyAddress);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always derive the compaction boundary from SafeReadOnlyAddress or a checkpointed commit address.","Never pass TailAddress under concurrent writers.","Clamp any externally supplied address before compaction."],"tags":["csharp","compaction","log-address","faster"],"backgroundTag":"invalid-argument-value","analyzedSha":"321d872eabda6a0345c8bd76419f89723ed864ae","analyzedAt":"2026-09-15T22:18:00.693Z","contentChangedAt":"2026-09-15T22:18:00.693Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}