static void RecoverAndTestStore(string path) { using var settings = new FasterKVSettings <MyKey, MyValue>(path, deleteDirOnDispose: true) { KeySerializer = () => new MyKeySerializer(), ValueSerializer = () => new MyValueSerializer(), TryRecoverLatest = true, }; using var store = new FasterKV <MyKey, MyValue>(settings); using var session = store.NewSession(new Functions()); // Test Read var key = new MyKey { key = 23 }; MyOutput g1 = default; var status = session.Read(ref key, ref g1); if (status.Found && g1.value.value == key.key) { Console.WriteLine("Success!"); } else { Console.WriteLine("Error!"); } }
static void PopulateStore(string path) { // We use class types MyKey and MyValue in this example, replace with blittable structs for // better performance. Serializers are required for class (non-blittable) types in order to // write to storage. You can also mark types as DataContract (lower performance). using var settings = new FasterKVSettings <MyKey, MyValue>(path) { KeySerializer = () => new MyKeySerializer(), ValueSerializer = () => new MyValueSerializer() }; using var store = new FasterKV <MyKey, MyValue>(settings); using var s = store.NewSession(new Functions()); for (int i = 0; i < 20000; i++) { var _key = new MyKey { key = i }; var value = new MyValue { value = i }; s.Upsert(ref _key, ref value); } var key = new MyKey { key = 23 }; MyOutput g1 = default; var status = s.Read(ref key, ref g1); if (status.Found && g1.value.value == key.key) { Console.WriteLine("Success!"); } else { Console.WriteLine("Error!"); } // Take index + fold-over checkpoint of FASTER, wait to complete store.TakeFullCheckpointAsync(CheckpointType.FoldOver).AsTask().GetAwaiter().GetResult(); }
static void InMemorySample() { Console.WriteLine("In-Memory Sample:\n"); long key = 1, value = 1, output = 0; // Create a default config (null path indicates in-memory only) // Uses default config parameters. Update config fields to tune parameters. using var config = new FasterKVSettings <long, long>(null); Console.WriteLine($"FasterKV config:\n{config}\n"); using var store = new FasterKV <long, long>(config); // Create functions for callbacks; we use a standard in-built function in this sample. // You can write your own by extending this or FunctionsBase. // In this in-built function, read-modify-writes will perform value merges via summation. var funcs = new SimpleFunctions <long, long>((a, b) => a + b); // Each logical sequence of calls to FASTER is associated with a FASTER session. // No concurrency allowed within a single session using var session = store.NewSession(funcs); // (1) Upsert and read back upserted value session.Upsert(ref key, ref value); // Reads are served back from memory and return synchronously var status = session.Read(ref key, ref output); if (status.Found && output == value) { Console.WriteLine("(1) Success!"); } else { Console.WriteLine("(1) Error!"); } /// (2) Delete key, read to verify deletion session.Delete(ref key); status = session.Read(ref key, ref output); if (status.Found) { Console.WriteLine("(2) Error!"); } else { Console.WriteLine("(2) Success!"); } // (4) Perform two read-modify-writes (summation), verify result key = 2; long input1 = 25, input2 = 27; session.RMW(ref key, ref input1); session.RMW(ref key, ref input2); status = session.Read(ref key, ref output); if (status.Found && output == input1 + input2) { Console.WriteLine("(3) Success!"); } else { Console.WriteLine("(3) Error!"); } // (5) Perform TryAdd using RMW and custom IFunctions using var tryAddSession = store.NewSession(new TryAddFunctions <long, long>()); key = 3; input1 = 30; input2 = 31; // First TryAdd - success; status should be NotFound (does not already exist) status = tryAddSession.RMW(ref key, ref input1); // Second TryAdd - failure; status should be Found (already exists) var status2 = tryAddSession.RMW(ref key, ref input2); // Read, result should be input1 (first TryAdd) var status3 = session.Read(ref key, ref output); if (status.NotFound && status2.Found && status3.Found && output == input1) { Console.WriteLine("(4) Success!"); } else { Console.WriteLine("(4) Error!"); } }
static void DiskSample() { Console.WriteLine("\nDisk Sample:\n"); long key = 1, value = 1, output = 0; // Create FasterKV config based on specified base directory path. using var config = new FasterKVSettings <long, long>("./database") { TryRecoverLatest = true }; Console.WriteLine($"FasterKV config:\n{config}\n"); // Create store using specified config using var store = new FasterKV <long, long>(config); // Create functions for callbacks; we use a standard in-built function in this sample. // You can write your own by extending this or FunctionsBase. // In this in-built function, read-modify-writes will perform value merges via summation. var funcs = new SimpleFunctions <long, long>((a, b) => a + b); // Each logical sequence of calls to FASTER is associated with a FASTER session. // No concurrency allowed within a single session using var session = store.NewSession(funcs); if (store.RecoveredVersion == 1) // did not recover { Console.WriteLine("Clean start; upserting key-value pair"); // (1) Upsert and read back upserted value session.Upsert(ref key, ref value); // Take checkpoint so data is persisted for recovery Console.WriteLine("Taking full checkpoint"); store.TryInitiateFullCheckpoint(out _, CheckpointType.Snapshot); store.CompleteCheckpointAsync().AsTask().GetAwaiter().GetResult(); } else { Console.WriteLine($"Recovered store to version {store.RecoveredVersion}"); } // Reads are served back from memory and return synchronously var status = session.Read(ref key, ref output); if (status.Found && output == value) { Console.WriteLine("(1) Success!"); } else { Console.WriteLine("(1) Error!"); } // (2) Force flush record to disk and evict from memory, so that next read is served from disk store.Log.FlushAndEvict(true); // Reads from disk will return PENDING status, result available via either asynchronous IFunctions callback // or on this thread via CompletePendingWithOutputs, shown below status = session.Read(ref key, ref output); if (status.IsPending) { session.CompletePendingWithOutputs(out var iter, true); while (iter.Next()) { if (iter.Current.Status.Found && iter.Current.Output == value) { Console.WriteLine("(2) Success!"); } else { Console.WriteLine("(2) Error!"); } } iter.Dispose(); } else { Console.WriteLine("(2) Error!"); } /// (3) Delete key, read to verify deletion session.Delete(ref key); status = session.Read(ref key, ref output); if (status.Found) { Console.WriteLine("(3) Error!"); } else { Console.WriteLine("(3) Success!"); } // (4) Perform two read-modify-writes (summation), verify result key = 2; long input1 = 25, input2 = 27; session.RMW(ref key, ref input1); session.RMW(ref key, ref input2); status = session.Read(ref key, ref output); if (status.Found && output == input1 + input2) { Console.WriteLine("(4) Success!"); } else { Console.WriteLine("(4) Error!"); } // (5) Perform TryAdd using RMW and custom IFunctions using var tryAddSession = store.NewSession(new TryAddFunctions <long, long>()); key = 3; input1 = 30; input2 = 31; // First TryAdd - success; status should be NOTFOUND (does not already exist) status = tryAddSession.RMW(ref key, ref input1); // Second TryAdd - failure; status should be OK (already exists) var status2 = tryAddSession.RMW(ref key, ref input2); // Read, result should be input1 (first TryAdd) var status3 = session.Read(ref key, ref output); if (!status.Found && status2.Found && status3.Found && output == input1) { Console.WriteLine("(5) Success!"); } else { Console.WriteLine("(5) Error!"); } }