예제 #1
0
        public unsafe void MultiReadSpanByteKeyTest()
        {
            using var log = Devices.CreateLogDevice(TestContext.CurrentContext.TestDirectory + "/MultiReadSpanByteKeyTest.log", deleteOnClose: true);
            using var fht = new FasterKV <SpanByte, long>(
                      size: 1L << 20,
                          new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 12 });
            using var session = fht.For(new MultiReadSpanByteKeyTestFunctions()).NewSession <MultiReadSpanByteKeyTestFunctions>();

            for (int i = 0; i < 3000; i++)
            {
                var key = MemoryMarshal.Cast <char, byte>($"{i}".AsSpan());

                fixed(byte *_ = key)
                session.Upsert(SpanByte.FromFixedSpan(key), i);
            }

            // Evict all records to disk
            fht.Log.FlushAndEvict(true);

            for (long key = 0; key < 50; key++)
            {
                // read each key multiple times
                for (int i = 0; i < 10; i++)
                {
                    Assert.AreEqual(key, ReadKey($"{key}"));
                }
            }

            long ReadKey(string keyString)
            {
                Status status;

                var key = MemoryMarshal.Cast <char, byte>(keyString.AsSpan());

                fixed(byte *_ = key)
                status = session.Read(key: SpanByte.FromFixedSpan(key), out var unused);

                // All keys need to be fetched from disk
                Assert.AreEqual(Status.PENDING, status);

                session.CompletePendingWithOutputs(out var completedOutputs, wait: true);

                var count = 0;
                var value = 0L;

                using (completedOutputs)
                {
                    while (completedOutputs.Next())
                    {
                        count++;
                        Assert.AreEqual(Status.OK, completedOutputs.Current.Status);
                        value = completedOutputs.Current.Output;
                    }
                }
                Assert.AreEqual(1, count);
                return(value);
            }
        }
예제 #2
0
            /// <summary>
            /// Puts a new record or replace the existing one in the store.
            /// </summary>
            /// <param name="key">The key of the record.</param>
            /// <param name="value">The record value.</param>
            public void Put(ulong key, ref Span <byte> value)
            {
                var status    = Status.OK;
                var context   = Context.WithUpsert(s => { status = s; });
                var fixedSpan = SpanByte.FromFixedSpan(value);

                status = _session?.Upsert(ref key, ref fixedSpan, context, 0) ?? Status.ERROR;
                if (status == Status.PENDING)
                {
                    _session?.CompletePending(true);
                }
                if (status == Status.ERROR)
                {
                    throw new InvalidOperationException("Error ocurred when inserting data.");
                }
            }
예제 #3
0
        public unsafe void SpanByteUnitTest1()
        {
            Span <byte> payload    = stackalloc byte[20];
            Span <byte> serialized = stackalloc byte[24];

            SpanByte sb = SpanByte.FromFixedSpan(payload);

            Assert.IsFalse(sb.Serialized);
            Assert.AreEqual(20, sb.Length);
            Assert.AreEqual(24, sb.TotalSize);
            Assert.AreEqual(20, sb.AsSpan().Length);
            Assert.AreEqual(20, sb.AsReadOnlySpan().Length);

            fixed(byte *ptr = serialized)
            sb.CopyTo(ptr);

            ref SpanByte ssb = ref SpanByte.ReinterpretWithoutLength(serialized);
예제 #4
0
        public unsafe void RandomReadCacheTest1()
        {
            TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true);
            var log = Devices.CreateLogDevice(TestUtils.MethodTestDir + "/BasicFasterTests.log", deleteOnClose: true);
            var fht = new FasterKV <SpanByte, long>(
                size: 1L << 20,
                    new LogSettings
                    {
                    LogDevice      = log,
                    MemorySizeBits = 15,
                    PageSizeBits   = 12,

                    ReadCacheSettings = new ReadCacheSettings
                    {
                        MemorySizeBits       = 15,
                        PageSizeBits         = 12,
                        SecondChanceFraction = 0.1,
                    }
                    });

            var session = fht.For(new Functions()).NewSession <Functions>();

            void Read(int i)
            {
                var keyString = $"{i}";
                var key       = MemoryMarshal.Cast <char, byte>(keyString.AsSpan());

                fixed(byte *_ = key)
                {
                    var  context = new Context();
                    var  sb      = SpanByte.FromFixedSpan(key);
                    long input   = i * 2;
                    long output  = 0;
                    var  status  = session.Read(ref sb, ref input, ref output, context);

                    if (status == Status.OK)
                    {
                        Assert.AreEqual(input, output);
                        return;
                    }

                    Assert.AreEqual(Status.PENDING, status, $"was not OK or PENDING: {keyString}");

                    session.CompletePending(wait: true);
                }
            }

            var num = 30000;

            // write the values
            for (int i = 0; i < num; i++)
            {
                var keyString = $"{i}";
                var key       = MemoryMarshal.Cast <char, byte>(keyString.AsSpan());
                fixed(byte *_ = key)
                {
                    var sb = SpanByte.FromFixedSpan(key);

                    Assert.AreEqual(Status.OK, session.Upsert(sb, i * 2));
                }
            }

            // read through the keys in order (works)
            for (int i = 0; i < num; i++)
            {
                Read(i);
            }

            // pick random keys to read
            var r = new Random(2115);

            for (int i = 0; i < num; i++)
            {
                Read(r.Next(num));
            }

            fht.Dispose();
            log.Dispose();
            TestUtils.DeleteDirectory(TestUtils.MethodTestDir);
        }
예제 #5
0
        /// <summary>
        /// This sample shows how our special type called SpanByte can be leverage to use FASTER
        /// with variable-length keys and/or values without a separate object log. SpanBytes can
        /// easily be created using pinned or fixed memory. A SpanByte is basically a sequence of
        /// bytes with a 4-byte integer length header that denotes the size of the payload.
        ///
        /// Underlying SpanByte is the use of "ref struct" as a proxy for pointers to variable-sized
        /// memory in C# (we call these VariableLengthStructs).
        /// </summary>
        static void SpanByteSample()
        {
            // VarLen types do not need an object log
            var log = Devices.CreateLogDevice("hlog.log", deleteOnClose: true);

            // Create store
            // For custom varlen (not SpanByte), you need to provide IVariableLengthStructSettings and IFasterEqualityComparer
            var store = new FasterKV <SpanByte, SpanByte>(
                size: 1L << 20,
                    logSettings: new LogSettings {
                LogDevice = log, MemorySizeBits = 15, PageSizeBits = 12
                    });

            // Create session
            var s = store.For(new CustomSpanByteFunctions(locking: false)).NewSession <CustomSpanByteFunctions>();

            Random r = new Random(100);

            // stackalloc implies fixed, so it can be used directly with SpanByte
            Span <byte> keyMem   = stackalloc byte[1000];
            Span <byte> valueMem = stackalloc byte[1000];

            for (byte i = 0; i < 200; i++)
            {
                var keyLen = r.Next(1, 1000);
                var key    = keyMem.Slice(0, keyLen);
                key.Fill(i);

                var valLen = r.Next(1, 1000);
                var value  = valueMem.Slice(0, valLen);
                value.Fill((byte)valLen);

                // Option 1: Using overload for Span<byte>
                s.Upsert(key, value);
            }

            using (IFasterScanIterator <SpanByte, SpanByte> iterator = store.Log.Scan(store.Log.BeginAddress, store.Log.TailAddress))
            {
                while (iterator.GetNext(out RecordInfo recordInfo, out SpanByte keyObj, out SpanByte valueObj))
                {
                    Console.WriteLine("Key: " + keyObj.ToByteArray());
                }
            }
            bool success = true;

            r = new Random(100);
            for (byte i = 0; i < 200; i++)
            {
                var         keyLen = r.Next(1, 1000);
                Span <byte> key    = keyMem.Slice(0, keyLen);
                key.Fill(i);

                var valLen = r.Next(1, 1000);

                // Option 2: Converting fixed Span<byte> to SpanByte
                var status = s.Read(SpanByte.FromFixedSpan(key), out byte[] output, userContext: (byte)valLen);

                var expectedValue = valueMem.Slice(0, valLen);
                expectedValue.Fill((byte)valLen);

                if (status == Status.PENDING)
                {
                    s.CompletePending(true);
                }
                else
                {
                    if ((status != Status.OK) || (!output.SequenceEqual(expectedValue.ToArray())))
                    {
                        success = false;
                        break;
                    }
                }
            }

            if (success)
            {
                Console.WriteLine("Success!");
            }
            else
            {
                Console.WriteLine("Error!");
            }

            s.Dispose();
            store.Dispose();
            log.Dispose();
        }
예제 #6
0
        public static void Run()
        {
            const bool useRmw     = true;
            const int  baseNumber = 45;

            // VarLen types do not need an object log
            using var log = Devices.CreateLogDevice("hlog.log", deleteOnClose: true);

            // Create store
            // For custom varlen (not SpanByte), you need to provide IVariableLengthStructSettings and IFasterEqualityComparer.
            // For this test we require record-level locking
            using var store = new FasterKV <SpanByte, SpanByte>(
                      size: 1L << 20,
                          logSettings: new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 12 }, disableLocking: false);

            // Create session for ASCII sums. We require two callback function types to be provided:
            //    AsciiSumSpanByteFunctions implements RMW callback functions
            //    AsciiSumVLS implements the callback for computing the length of the result new value, given an old value and an input
            using var s = store.For(new AsciiSumSpanByteFunctions()).NewSession <AsciiSumSpanByteFunctions>
                              (sessionVariableLengthStructSettings: new SessionVariableLengthStructSettings <SpanByte, SpanByte> { valueLength = new AsciiSumVLS() });

            // Create key
            Span <byte> key = stackalloc byte[10];

            key.Fill(23);
            var _key = SpanByte.FromFixedSpan(key);

            // Create input
            Span <byte> input       = stackalloc byte[10];
            int         inputLength = Utils.LongToBytes(baseNumber, input);
            var         _input      = SpanByte.FromFixedSpan(input.Slice(0, inputLength));

            if (useRmw)
            {
                s.RMW(_key, _input);               // InitialUpdater to 45 (baseNumber)
                s.RMW(_key, _input);               // InPlaceUpdater to 90
                s.RMW(_key, _input);               // CopyUpdater to 135 (due to value size increase)
                s.RMW(_key, _input);               // InPlaceUpdater to 180

                store.Log.Flush(true);             // Flush by moving ReadOnly address to Tail (retain records in memory)
                s.RMW(_key, _input);               // CopyUpdater to 225 (due to immutable source value in read-only region)

                store.Log.FlushAndEvict(true);     // Flush and evict all records to disk
                var _status = s.RMW(_key, _input); // CopyUpdater to 270 (due to immutable source value on disk)

                if (_status.IsPending)
                {
                    Console.WriteLine("Error!");
                    return;
                }
                s.CompletePending(true); // Wait for IO completion
            }
            else
            {
                s.Upsert(_key, _input);
            }

            // Create output space
            Span <byte> output        = stackalloc byte[10];
            var         outputWrapper = new SpanByteAndMemory(SpanByte.FromFixedSpan(output));

            var status = s.Read(ref _key, ref outputWrapper);

            // Read does not go pending, and the output should fit in the provided space (10 bytes)
            // Hence, no Memory will be allocated by FASTER
            if (!status.Found || !outputWrapper.IsSpanByte)
            {
                Console.WriteLine("Error!");
                return;
            }

            // Check result value correctness
            if (useRmw)
            {
                inputLength = Utils.LongToBytes(baseNumber * 6, input);
            }

            if (!outputWrapper.SpanByte.AsReadOnlySpan().SequenceEqual(input.Slice(0, inputLength)))
            {
                Console.WriteLine("Error!");
                return;
            }

            Console.WriteLine("AsciiSumSample: Success!");
        }