/// <summary> /// Signup a student to a class /// </summary> public async Task Signup(IFdbTransaction tr, string s, string c) { var rec = AttendsKey(s, c); if ((await tr.GetAsync(rec)).IsPresent) { // already signed up return; } int seatsLeft = Int32.Parse((await tr.GetAsync(ClassKey(c))).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), Slice.FromStringAscii((seatsLeft - 1).ToString())); tr.Set(rec, Slice.Empty); }
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); }
/// <summary>Get and pops the last item off the Vector.</summary> public async Task <(T Value, bool HasValue)> PopAsync(IFdbTransaction tr) { if (tr == null) { throw new ArgumentNullException(nameof(tr)); } var keyRange = this.Subspace.ToRange(); // Read the last two entries so we can check if the second to last item // is being represented sparsely. If so, we will be required to set it // to the default value var lastTwo = await tr .GetRange(keyRange, new FdbRangeOptions { Reverse = true, Limit = 2 }) .ToListAsync() .ConfigureAwait(false); // Vector was empty if (lastTwo.Count == 0) { return(default);
/// <summary>Get and pops the last item off the Vector.</summary> public async Task <Optional <T> > PopAsync([NotNull] IFdbTransaction tr) { if (tr == null) { throw new ArgumentNullException("tr"); } var keyRange = this.Subspace.Keys.ToRange(); // Read the last two entries so we can check if the second to last item // is being represented sparsely. If so, we will be required to set it // to the default value var lastTwo = await tr .GetRange(keyRange, new FdbRangeOptions { Reverse = true, Limit = 2 }) .ToListAsync() .ConfigureAwait(false); // Vector was empty if (lastTwo.Count == 0) { return(default(Optional <T>)); } //note: keys are reversed so indices[0] = last, indices[1] = second to last var indices = lastTwo.Select(kvp => this.Subspace.Keys.DecodeFirst <long>(kvp.Key)).ToList(); if (indices[0] == 0) { // Vector has size one //pass } else if (lastTwo.Count == 1 || indices[0] > indices[1] + 1) { // Second to last item is being represented sparsely tr.Set(GetKeyAt(indices[0] - 1), this.Encoder.EncodeValue(this.DefaultValue)); } tr.Clear(lastTwo[0].Key); return(this.Encoder.DecodeValue(lastTwo[0].Value)); }
private async Task<KeyValuePair<Slice, Slice>> FindRandomItem(IFdbTransaction tr, FdbSubspace ring) { var range = ring.ToRange(); // start from a random position around the ring Slice key = ring.Pack(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 virtual FdbRangeQuery <KeyValuePair <Slice, Slice> > GetRange(KeySelector beginInclusive, KeySelector endExclusive, FdbRangeOptions options = null) { ThrowIfDisposed(); return(m_transaction.GetRange(beginInclusive, endExclusive, options)); }
internal async Task<FdbDirectorySubspace> CreateOrOpenInternalAsync(IFdbReadOnlyTransaction readTrans, IFdbTransaction trans, [NotNull] IFdbTuple path, Slice layer, Slice prefix, bool allowCreate, bool allowOpen, bool throwOnError) { Contract.Requires(readTrans != null || trans != null, "Need at least one transaction"); Contract.Requires(path != null, "Path must be specified"); Contract.Requires(readTrans == null || trans == null || object.ReferenceEquals(readTrans, trans), "The write transaction should be the same as the read transaction"); if (path.Count == 0) { // Root directory contains node metadata and so may not be opened. throw new InvalidOperationException("The root directory may not be opened."); } // to open an existing directory, we only need the read transaction // if none was specified, we can use the writeable transaction if (readTrans == null) readTrans = trans; await CheckReadVersionAsync(readTrans).ConfigureAwait(false); if (prefix.HasValue && this.Path.Count > 0) throw new InvalidOperationException("Cannot specify a prefix in a partition."); var existingNode = await FindAsync(readTrans, path).ConfigureAwait(false); if (existingNode.Exists) { if (existingNode.IsInPartition(false)) { var subpath = existingNode.PartitionSubPath; var dl = GetPartitionForNode(existingNode).DirectoryLayer; return await dl.CreateOrOpenInternalAsync(readTrans, trans, subpath, layer, prefix, allowCreate, allowOpen, throwOnError).ConfigureAwait(false); } if (!allowOpen) { if (throwOnError) throw new InvalidOperationException(string.Format("The directory {0} already exists.", path)); return null; } if (layer.IsPresent && layer != existingNode.Layer) { throw new InvalidOperationException(String.Format("The directory {0} was created with incompatible layer {1} instead of expected {2}.", path, layer.ToAsciiOrHexaString(), existingNode.Layer.ToAsciiOrHexaString())); } return ContentsOfNode(existingNode.Subspace, path, existingNode.Layer); } if (!allowCreate) { if (throwOnError) throw new InvalidOperationException(string.Format("The directory {0} does not exist.", path)); return null; } // from there, we actually do need a wrtieable transaction if (trans == null) throw new InvalidOperationException("A writeable transaction is needed to create a new directory"); await CheckWriteVersionAsync(trans).ConfigureAwait(false); if (prefix == null) { // automatically allocate a new prefix inside the ContentSubspace long id = await this.Allocator.AllocateAsync(trans).ConfigureAwait(false); prefix = this.ContentSubspace.Pack(id); // ensure that there is no data already present under this prefix if (FdbDirectoryLayer.AnnotateTransactions) trans.Annotate("Ensure that there is no data already present under prefix {0}", prefix); if (await trans.GetRange(FdbKeyRange.StartsWith(prefix)).AnyAsync().ConfigureAwait(false)) { throw new InvalidOperationException(String.Format("The database has keys stored at the prefix chosen by the automatic prefix allocator: {0}", prefix.ToAsciiOrHexaString())); } // ensure that the prefix has not already been allocated if (FdbDirectoryLayer.AnnotateTransactions) trans.Annotate("Ensure that the prefix {0} has not already been allocated", prefix); if (!(await IsPrefixFree(trans.Snapshot, prefix).ConfigureAwait(false))) { throw new InvalidOperationException("The directory layer has manually allocated prefixes that conflict with the automatic prefix allocator."); } } else { if (FdbDirectoryLayer.AnnotateTransactions) trans.Annotate("Ensure that the prefix {0} hasn't already been allocated", prefix); // ensure that the prefix has not already been allocated if (!(await IsPrefixFree(trans, prefix).ConfigureAwait(false))) { throw new InvalidOperationException("The given prefix is already in use."); } } // we need to recursively create any missing parents FdbSubspace parentNode; if (path.Count > 1) { var parentSubspace = await CreateOrOpenInternalAsync(readTrans, trans, path.Substring(0, path.Count - 1), Slice.Nil, Slice.Nil, true, true, true).ConfigureAwait(false); parentNode = NodeWithPrefix(parentSubspace.Key); } else { parentNode = this.RootNode; } if (parentNode == null) throw new InvalidOperationException(string.Format("The parent directory of {0} doesn't exist.", path)); // initialize the metadata for this new directory var node = NodeWithPrefix(prefix); if (FdbDirectoryLayer.AnnotateTransactions) trans.Annotate("Registering the new prefix {0} into the folder sub-tree", prefix); trans.Set(GetSubDirKey(parentNode, path.Get<string>(-1)), prefix); SetLayer(trans, node, layer); return ContentsOfNode(node, path, layer); }
/// <summary> /// Signup a student to a class /// </summary> public async Task Signup(IFdbTransaction tr, string s, string c) { var rec = AttendsKey(s, c); if ((await tr.GetAsync(rec)).IsPresent) { // already signed up return; } int seatsLeft = Int32.Parse((await tr.GetAsync(ClassKey(c))).ToAscii()); 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), Slice.FromAscii((seatsLeft - 1).ToString())); tr.Set(rec, Slice.Empty); }