/// <summary> /// Signup a student to a class /// </summary> public async Task Signup(IFdbTransaction tr, string s, string c, IDynamicKeySubspace subspace) { var rec = AttendsKey(s, c); if ((await tr.GetAsync(rec)).IsPresent) { // already signed up return; } int seatsLeft = Int32.Parse((await tr.GetAsync(ClassKey(c, subspace))).ToStringAscii()); if (seatsLeft <= 0) { throw new InvalidOperationException("No remaining seats"); } var classes = await tr.GetRange(AttendsKeys(s)).ToListAsync(); if (classes.Count >= 5) { throw new InvalidOperationException("Too many classes"); } tr.Set(ClassKey(c, subspace), Slice.FromStringAscii((seatsLeft - 1).ToString())); tr.Set(rec, Slice.Empty); }
/// <summary>Create an allocator operating under a specific location</summary> /// <param name="subspace"></param> public FdbHighContentionAllocator(IDynamicKeySubspace subspace) { Contract.NotNull(subspace, nameof(subspace)); this.Subspace = subspace; this.Counters = subspace.Partition.ByKey(COUNTERS); this.Recent = subspace.Partition.ByKey(RECENT); }
/// <summary>Create a new sparse Vector</summary> /// <param name="subspace">Subspace where the vector will be stored</param> /// <param name="defaultValue">Default value for sparse entries</param> /// <param name="encoder">Encoder used for the values of this vector</param> public FdbVector([NotNull] IDynamicKeySubspace subspace, T defaultValue, IValueEncoder <T> encoder = null) { if (subspace == null) { throw new ArgumentNullException(nameof(subspace)); } this.Subspace = subspace; this.DefaultValue = defaultValue; this.Encoder = encoder ?? TuPack.Encoding.GetValueEncoder <T>(); }
/// <summary>Create an allocator operating under a specific location</summary> /// <param name="subspace"></param> public FdbHighContentionAllocator(IDynamicKeySubspace subspace) { if (subspace == null) { throw new ArgumentNullException(nameof(subspace)); } this.Subspace = subspace; this.Counters = subspace.Partition.ByKey(COUNTERS); this.Recent = subspace.Partition.ByKey(RECENT); }
/// <summary>Change the current global namespace.</summary> /// <remarks>Do NOT call this, unless you know exactly what you are doing !</remarks> internal void ChangeRoot(IKeySubspace subspace, IFdbDirectory directory, bool readOnly) { //REVIEW: rename to "ChangeRootSubspace" ? subspace = subspace ?? KeySubspace.Empty; lock (this) //TODO: don't use this for locking { m_readOnly = readOnly; m_globalSpace = subspace.Copy(TuPack.Encoding); m_globalSpaceCopy = subspace.Copy(TuPack.Encoding); // keep another copy m_directory = directory; } }
/// <summary> /// Drop a student from a class /// </summary> public async Task Drop(IFdbTransaction tr, string s, string c, IDynamicKeySubspace subspace) { var rec = AttendsKey(s, c); if ((await tr.GetAsync(rec)).IsNullOrEmpty) { // not taking this class return; } var students = Int32.Parse((await tr.GetAsync(ClassKey(c, subspace))).ToStringAscii()); tr.Set(ClassKey(c, subspace), Slice.FromStringAscii((students + 1).ToString())); tr.Clear(rec); }
private async Task PushQueueAsync(IFdbTransaction tr, IDynamicKeySubspace queue, Slice taskId) { //TODO: use a high contention algo ? // - must support Push and Pop // - an empty queue must correspond to an empty subspace // get the current size of the queue var range = queue.Keys.ToRange(); var lastKey = await tr.Snapshot.GetKeyAsync(KeySelector.LastLessThan(range.End)).ConfigureAwait(false); int count = lastKey < range.Begin ? 0 : queue.Keys.DecodeFirst <int>(lastKey) + 1; // set the value tr.Set(queue.Keys.Encode(count, GetRandomId()), taskId); }
/// <summary> /// Setup the initial state of the database /// </summary> public async Task Init(IFdbDatabase db, CancellationToken ct) { // open the folder where we will store everything this.Subspace = await db.Directory.CreateOrOpenAsync(new [] { "Benchmarks", "LeakTest" }, ct : ct); // clear all previous values await db.ClearRangeAsync(this.Subspace, ct); // insert all the classes await db.WriteAsync((tr) => { tr.Set(this.Subspace.GetPrefix() + FdbKey.MinValue, Slice.FromString("BEGIN")); tr.Set(this.Subspace.GetPrefix() + FdbKey.MaxValue, Slice.FromString("END")); }, ct); }
/// <summary>Create a new queue using either High Contention mode or Simple mode</summary> /// <param name="subspace">Subspace where the queue will be stored</param> /// <param name="highContention">If true, uses High Contention Mode (lots of popping clients). If true, uses the Simple Mode (a few popping clients).</param> /// <param name="encoder">Encoder for the values stored in this queue</param> public FdbQueue([NotNull] IDynamicKeySubspace subspace, bool highContention = false, IValueEncoder <T> encoder = null) { if (subspace == null) { throw new ArgumentNullException(nameof(subspace)); } this.Subspace = subspace; this.HighContention = highContention; this.Encoder = encoder ?? TuPack.Encoding.GetValueEncoder <T>(); //TODO: rewrite this, using FdbEncoderSubpsace<..> ! this.ConflictedPop = this.Subspace.Partition.ByKey(Slice.FromStringAscii("pop")); this.ConflictedItem = this.Subspace.Partition.ByKey(Slice.FromStringAscii("conflict")); this.QueueItem = this.Subspace.Partition.ByKey(Slice.FromStringAscii("item")); }
/// <summary> /// Setup the initial state of the database /// </summary> public async Task Init(IFdbDatabase db, CancellationToken ct) { this.Subspace = await db.ReadWriteAsync(async tr => { // open the folder where we will store everything var subspace = await db.Root["Benchmarks"]["LeakTest"].CreateOrOpenAsync(tr); // clear all previous values await db.ClearRangeAsync(subspace, ct); // insert all the classes tr.Set(subspace.GetPrefix() + FdbKey.MinValue, Slice.FromString("BEGIN")); tr.Set(subspace.GetPrefix() + FdbKey.MaxValue, Slice.FromString("END")); return(subspace); }, ct); }
/// <summary> /// Setup the initial state of the database /// </summary> public async Task Init(IFdbDatabase db, CancellationToken ct) { // open the folder where we will store everything this.Subspace = await db.Directory.CreateOrOpenAsync(new [] { "Tutorials", "ClassScheduling" }, ct : ct); // clear all previous values await db.ClearRangeAsync(this.Subspace, ct); // insert all the classes await db.WriteAsync((tr) => { foreach (var c in this.ClassNames) { tr.Set(ClassKey(c), Slice.FromStringAscii("100")); } }, ct); }
/// <summary>Create a new High Contention counter, using a specific value encoder.</summary> /// <param name="db">Database used by this layer</param> /// <param name="subspace">Subspace to be used for storing the counter</param> /// <param name="encoder">Encoder for the counter values</param> public FdbHighContentionCounter([NotNull] IFdbDatabase db, [NotNull] IDynamicKeySubspace subspace, [NotNull] IValueEncoder <long> encoder) { if (db == null) { throw new ArgumentNullException(nameof(db)); } if (subspace == null) { throw new ArgumentNullException(nameof(subspace)); } if (encoder == null) { throw new ArgumentNullException(nameof(encoder)); } this.Database = db; this.Subspace = subspace.AsDynamic(); this.Encoder = encoder; }
/// <summary> /// Setup the initial state of the database /// </summary> public async Task Init(IFdbDatabase db, CancellationToken ct) { // open the folder where we will store everything this.Subspace = await db.ReadWriteAsync(async tr => { var subspace = await db.Root["Tutorials"]["ClassScheduling"].CreateOrOpenAsync(tr); // clear all previous values tr.ClearRange(subspace); // insert all the classes foreach (var c in this.ClassNames) { tr.Set(ClassKey(c), Slice.FromStringAscii("100")); } return(subspace); }, ct); }
public async Task Run(IFdbDatabase db, TextWriter log, CancellationToken ct) { const int STUDENTS = 10; const int OPS_PER_STUDENTS = 10; this.Subspace = db.GlobalSpace; await Init(db, ct); log.WriteLine("# Class sheduling test initialized"); // run multiple students var elapsed = await Program.RunConcurrentWorkersAsync( STUDENTS, (i, _ct) => IndecisiveStudent(db, i, OPS_PER_STUDENTS, _ct), ct ); log.WriteLine("# Ran {0} transactions in {1:0.0##} sec", (STUDENTS * OPS_PER_STUDENTS), elapsed.TotalSeconds); }
private static async Task BenchSerialWriteAsync(IFdbDatabase db, int N, CancellationToken ct) { // read a lot of small keys, one by one Console.WriteLine($"=== BenchSerialWrite(N={N:N0}) ==="); var location = db.Root.ByKey("hello"); var sw = Stopwatch.StartNew(); IFdbTransaction trans = null; IDynamicKeySubspace subspace = null; try { for (int i = 0; i < N; i++) { if (trans == null) { trans = await db.BeginTransactionAsync(ct); subspace = await location.Resolve(trans); } trans.Set(subspace.Encode(i), Slice.FromInt32(i)); if (trans.Size > 100 * 1024) { await trans.CommitAsync(); trans.Dispose(); trans = null; } } await trans.CommitAsync(); } finally { trans?.Dispose(); } sw.Stop(); Console.WriteLine($"Took {sw.Elapsed.TotalSeconds:N3} sec to read {N:N0} items ({FormatTimeMicro(sw.Elapsed.TotalMilliseconds / N)}/read, {N/sw.Elapsed.TotalSeconds:N0} read/sec)"); Console.WriteLine(); }
/// <summary> /// Drop a student from a class, and sign him up to another class /// </summary> public async Task Switch(IFdbTransaction tr, string s, string oldC, string newC, IDynamicKeySubspace subspace) { await Drop(tr, s, oldC, subspace); await Signup(tr, s, newC, subspace); }
private async Task RunAsync(IFdbDatabase db, IDynamicKeySubspace location, CancellationToken ct, Action done, int N, int K, int W) { if (db == null) { throw new ArgumentNullException(nameof(db)); } StringBuilder sb = new StringBuilder(); db = new FdbLoggedDatabase(db, false, false, (log) => { sb.AppendLine(log.Log.GetTimingsReport(true)); //Console.WriteLine(log.Log.GetTimingsReport(true)); }); try { var workerPool = new FdbWorkerPool(location); Console.WriteLine($"workerPool at {location.GetPrefix():P}"); var workerSignal = new AsyncCancelableMutex(ct); var clientSignal = new AsyncCancelableMutex(ct); int taskCounter = 0; int msgSent = 0; int msgReceived = 0; Func <FdbWorkerMessage, CancellationToken, Task> handler = async(msg, _ct) => { Interlocked.Increment(ref msgReceived); //await Task.Delay(10 + Math.Abs(msg.Id.GetHashCode()) % 50); await Task.Delay(10).ConfigureAwait(false); }; Func <int, Task> worker = async(id) => { await workerSignal.Task.ConfigureAwait(false); Console.WriteLine("Worker #" + id + " is starting"); try { await workerPool.RunWorkerAsync(db, handler, ct).ConfigureAwait(false); } finally { Console.WriteLine("Worker #" + id + " has stopped"); } }; Func <int, Task> client = async(id) => { await clientSignal.Task.ConfigureAwait(false); await Task.Delay(10).ConfigureAwait(false); var rnd = new Random(id * 111); for (int i = 0; i < N; i++) { var taskId = Slice.FromString("T" + Interlocked.Increment(ref taskCounter)); var taskBody = Slice.FromString("Message " + (i + 1) + " of " + N + " from client #" + id); await workerPool.ScheduleTaskAsync(db, taskId, taskBody, ct).ConfigureAwait(false); Interlocked.Increment(ref msgSent); //if (i > 0 && i % 10 == 0) Console.WriteLine("@@@ Client#" + id + " pushed " + (i + 1) + " / " + N + " messages"); switch (rnd.Next(5)) { case 0: await Task.Delay(10).ConfigureAwait(false); break; case 1: await Task.Delay(100).ConfigureAwait(false); break; case 2: await Task.Delay(500).ConfigureAwait(false); break; } } Console.WriteLine("@@@ Client#" + id + " has finished!"); }; Func <string, Task> dump = async(label) => { Console.WriteLine($"<dump label=\'{label}\' key=\'{location.GetPrefix():P}\'>"); using (var tr = db.BeginTransaction(ct)) { await tr.Snapshot .GetRange(KeyRange.StartsWith(location.GetPrefix())) .ForEachAsync((kvp) => { Console.WriteLine($" - {location.Keys.Unpack(kvp.Key)} = {kvp.Value:V}"); }).ConfigureAwait(false); } Console.WriteLine("</dump>"); }; var workers = Enumerable.Range(0, W).Select((i) => worker(i)).ToArray(); var clients = Enumerable.Range(0, K).Select((i) => client(i)).ToArray(); DateTime start = DateTime.Now; DateTime last = start; int lastHandled = -1; using (var timer = new Timer((_) => { var now = DateTime.Now; Console.WriteLine("@@@ T=" + now.Subtract(start) + ", sent: " + msgSent.ToString("N0") + ", recv: " + msgReceived.ToString("N0")); Console.WriteLine("### Workers: " + workerPool.IdleWorkers + " / " + workerPool.ActiveWorkers + " (" + new string('#', workerPool.IdleWorkers) + new string('.', workerPool.ActiveWorkers - workerPool.IdleWorkers) + "), sent: " + workerPool.MessageScheduled.ToString("N0") + ", recv: " + workerPool.MessageReceived.ToString("N0") + ", delta: " + (workerPool.MessageScheduled - workerPool.MessageReceived).ToString("N0") + ", busy: " + workerPool.WorkerBusyTime + " (avg " + workerPool.WorkerAverageBusyDuration.TotalMilliseconds.ToString("N3") + " ms)"); if (now.Subtract(last).TotalSeconds >= 10) { //dump("timer").GetAwaiter().GetResult(); last = now; if (lastHandled == msgReceived) { // STALL ? Console.WriteLine("STALL! "); done(); } lastHandled = msgReceived; } if (msgReceived >= K * N) { dump("complete").GetAwaiter().GetResult(); done(); } }, null, 1000, 1000)) { var sw = Stopwatch.StartNew(); // start the workers workerSignal.Set(async: true); await Task.Delay(500); await dump("workers started"); // start the clients clientSignal.Set(async: true); await Task.WhenAll(clients); Console.WriteLine("Clients completed after " + sw.Elapsed); await Task.WhenAll(workers); Console.WriteLine("Workers completed after " + sw.Elapsed); } } finally { Console.WriteLine("---------------------------------------------------------------------------"); Console.WriteLine("Transaction logs:"); Console.WriteLine(); Console.WriteLine(sb.ToString()); } }
public static DynamicKeySubspace Copy(this IDynamicKeySubspace subspace, ISubspaceContext?context = null) { Contract.NotNull(subspace); return(new DynamicKeySubspace(StealPrefix(subspace), subspace.KeyEncoder, context ?? subspace.Context)); }
internal DynamicPartition([NotNull] DynamicKeySubspace subspace) { Contract.Requires(subspace != null); this.Subspace = subspace; }
protected Slice ClassKey(string c, IDynamicKeySubspace subspace) { return(subspace.Keys.Encode("class", c)); }
public static DynamicKeySubspace Copy([NotNull] this IDynamicKeySubspace subspace) { Contract.NotNull(subspace, nameof(subspace)); return(new DynamicKeySubspace(StealPrefix(subspace), subspace.Encoding)); }
private async Task <KeyValuePair <Slice, Slice> > FindRandomItem(IFdbTransaction tr, IDynamicKeySubspace ring) { var range = ring.Keys.ToRange(); // start from a random position around the ring Slice key = ring.Keys.Encode(GetRandomId()); // We want to find the next item in the clockwise direction. If we reach the end of the ring, we "wrap around" by starting again from the start // => So we do find_next(key <= x < MAX) and if that does not produce any result, we do a find_next(MIN <= x < key) // When the ring only contains a few items (or is empty), there is more than 50% change that we wont find anything in the first read. // To reduce the latency for this case, we will issue both range reads at the same time, and discard the second one if the first returned something. // This should reduce the latency in half when the ring is empty, or when it contains only items before the random key. var candidate = await tr.GetRange(key, range.End).FirstOrDefaultAsync(); if (!candidate.Key.IsPresent) { candidate = await tr.GetRange(range.Begin, key).FirstOrDefaultAsync(); } return(candidate); }
public State(IDynamicKeySubspace subspace) { Contract.Debug.Requires(subspace != null); this.Subspace = subspace; }
public State(IDynamicKeySubspace subspace) { this.Subspace = subspace; }
internal State(IDynamicKeySubspace subspace, T defaultValue, IValueEncoder <T> encoder) { this.Subspace = subspace; this.DefaultValue = defaultValue; this.Encoder = encoder; }
internal State(FdbStringIntern layer, IDynamicKeySubspace subspace) { this.Layer = layer; this.Subspace = subspace; }