/// <summary> /// Compact the log until specified address, moving active records to the tail of the log. /// </summary> /// <param name="functions">Functions used to manage key-values during compaction</param> /// <param name="cf">User provided compaction functions (see <see cref="ICompactionFunctions{Key, Value}"/>).</param> /// <param name="untilAddress">Compact log until this address</param> /// <param name="shiftBeginAddress">Whether to shift begin address to untilAddress after compaction. To avoid /// data loss on failure, set this to false, and shift begin address only after taking a checkpoint. This /// ensures that records written to the tail during compaction are first made stable.</param> /// <returns>Address until which compaction was done</returns> public long Compact <Input, Output, Context, Functions, CompactionFunctions>(Functions functions, CompactionFunctions cf, long untilAddress, bool shiftBeginAddress) where Functions : IFunctions <Key, Value, Input, Output, Context> where CompactionFunctions : ICompactionFunctions <Key, Value> { if (untilAddress > fht.Log.SafeReadOnlyAddress) { throw new FasterException("Can compact only until Log.SafeReadOnlyAddress"); } var originalUntilAddress = untilAddress; var lf = new LogCompactionFunctions <Key, Value, Input, Output, Context, Functions>(functions); using var fhtSession = fht.For(lf).NewSession <LogCompactionFunctions <Key, Value, Input, Output, Context, Functions> >(); VariableLengthStructSettings <Key, Value> variableLengthStructSettings = null; if (allocator is VariableLengthBlittableAllocator <Key, Value> varLen) { variableLengthStructSettings = new VariableLengthStructSettings <Key, Value> { keyLength = varLen.KeyLength, valueLength = varLen.ValueLength, }; } using (var tempKv = new FasterKV <Key, Value>(fht.IndexSize, new LogSettings { LogDevice = new NullDevice(), ObjectLogDevice = new NullDevice() }, comparer: fht.Comparer, variableLengthStructSettings: variableLengthStructSettings)) using (var tempKvSession = tempKv.NewSession <Input, Output, Context, Functions>(functions)) { using (var iter1 = fht.Log.Scan(fht.Log.BeginAddress, untilAddress)) { while (iter1.GetNext(out var recordInfo)) { ref var key = ref iter1.GetKey(); ref var value = ref iter1.GetValue(); if (recordInfo.Tombstone || cf.IsDeleted(key, value)) { tempKvSession.Delete(ref key, default, 0);