/// <summary> /// Returns the list of names of all existing classes /// </summary> public Task <List <string> > AvailableClasses(IFdbReadOnlyTransaction tr) { return(tr.GetRange(this.Subspace.Keys.ToRange(STuple.Create("class"))) .Where(kvp => { int _; return Int32.TryParse(kvp.Value.ToStringAscii(), out _); }) // (step 3) .Select(kvp => this.Subspace.Keys.Decode <string>(kvp.Key)) .ToListAsync()); }
public static async Task <FdbSystemStatus?> GetStatusAsync(IFdbReadOnlyTransaction trans) { Contract.NotNull(trans); var data = await trans.GetAsync(StatusJsonKey).ConfigureAwait(false); if (data.IsNullOrEmpty) { return(null); } var doc = TinyJsonParser.ParseObject(data); if (doc == null) { return(null); } long rv = 0; if (doc.ContainsKey("cluster")) { rv = await trans.GetReadVersionAsync(); } return(new FdbSystemStatus(doc, rv, data)); }
/// <summary>Return the long string associated with the normalized representation <paramref name="uid"/></summary> public Task <string> LookupAsync(IFdbReadOnlyTransaction trans, Slice uid) { if (trans == null) { throw new ArgumentNullException("trans"); } if (uid.IsNull) { throw new ArgumentException("String uid cannot be nil", "uid"); } if (uid.IsEmpty) { return(Task.FromResult(String.Empty)); } string value; if (m_uidStringCache.TryGetValue(new Uid(uid), out value)) { return(Task.FromResult(value)); } return(LookupSlowAsync(trans, uid)); }
public PagingIterator(FdbRangeQuery <T> query, IFdbReadOnlyTransaction transaction) { Contract.Requires(query != null); this.Query = query; this.Transaction = transaction ?? query.Transaction; }
internal static FdbLoggedTransaction GetLogger(IFdbReadOnlyTransaction trans) { //TODO: the logged transaction could also be wrapped in other filters. // => we need a recursive "FindFilter<TFilter>" method that would unwrap the filter onion looking for a specific one... return trans as FdbLoggedTransaction; }
internal static FdbLoggedTransaction GetLogger([NotNull] IFdbReadOnlyTransaction trans) { //TODO: the logged transaction could also be wrapped in other filters. // => we need a recursive "FindFilter<TFilter>" method that would unwrap the filter onion looking for a specific one... return(trans as FdbLoggedTransaction); }
/// <summary> /// Returns the list of names of all existing classes /// </summary> public Task<List<string>> AvailableClasses(IFdbReadOnlyTransaction tr) { return tr.GetRange(this.Subspace.Keys.ToRange(FdbTuple.Create("class"))) .Where(kvp => { int _; return Int32.TryParse(kvp.Value.ToAscii(), out _); }) // (step 3) .Select(kvp => this.Subspace.Keys.Decode<string>(kvp.Key)) .ToListAsync(); }
/// <summary>Checks if a (key, value) pair exists</summary> public async Task<bool> ContainsAsync([NotNull] IFdbReadOnlyTransaction trans, TKey key, TValue value) { if (trans == null) throw new ArgumentNullException(nameof(trans)); var v = await trans.GetAsync(this.Subspace.Keys[key, value]).ConfigureAwait(false); return this.AllowNegativeValues ? v.IsPresent : v.ToInt64() > 0; }
/// <summary>Get the value of the next item in the queue without popping it.</summary> public async Task <(T Value, bool HasValue)> PeekAsync([NotNull] IFdbReadOnlyTransaction tr) { var firstItem = await GetFirstItemAsync(tr).ConfigureAwait(false); if (firstItem.Key.IsNull) { return(default);
/// <summary>Get the item at the specified index.</summary> public async Task <T> GetAsync([NotNull] IFdbReadOnlyTransaction tr, long index) { if (tr == null) { throw new ArgumentNullException("tr"); } if (index < 0) { throw new IndexOutOfRangeException(String.Format("Index {0} must be positive", index)); } var start = GetKeyAt(index); var end = this.Subspace.Keys.ToRange().End; var output = await tr .GetRange(start, end) .FirstOrDefaultAsync() .ConfigureAwait(false); if (output.Key.HasValue) { if (output.Key == start) { // The requested index had an associated key return(this.Encoder.DecodeValue(output.Value)); } // The requested index is sparsely represented return(this.DefaultValue); } // We requested a value past the end of the vector throw new IndexOutOfRangeException(String.Format("Index {0} out of range", index)); }
/// <summary>Return one or more fields of an hashset</summary> /// <param name="trans">Transaction that will be used for this request</param> /// <param name="id">Unique identifier of the hashset</param> /// <param name="fields">List of the fields to read</param> /// <returns>Dictionary containing the values of the selected fields, or Slice.Empty if that particular field does not exist.</returns> public async Task <IDictionary <string, Slice> > GetAsync(IFdbReadOnlyTransaction trans, IFdbTuple id, string[] fields) { if (trans == null) { throw new ArgumentNullException("trans"); } if (id == null) { throw new ArgumentNullException("id"); } if (fields == null) { throw new ArgumentNullException("fields"); } var keys = FdbTuple.PackRange(GetKey(id), fields); var values = await trans.GetValuesAsync(keys).ConfigureAwait(false); Contract.Assert(values != null && values.Length == fields.Length); var results = new Dictionary <string, Slice>(values.Length, StringComparer.OrdinalIgnoreCase); for (int i = 0; i < fields.Length; i++) { results[fields[i]] = values[i]; } return(results); }
/// <summary>Return all fields of an hashset</summary> /// <param name="trans">Transaction that will be used for this request</param> /// <param name="id">Unique identifier of the hashset</param> /// <returns>Dictionary containing, for all fields, their associated values</returns> public async Task <IDictionary <string, Slice> > GetAsync(IFdbReadOnlyTransaction trans, IVarTuple id) { if (trans == null) { throw new ArgumentNullException(nameof(trans)); } if (id == null) { throw new ArgumentNullException(nameof(id)); } var prefix = GetKey(id); var results = new Dictionary <string, Slice>(StringComparer.OrdinalIgnoreCase); await trans .GetRange(KeyRange.StartsWith(prefix)) .ForEachAsync((kvp) => { string field = this.Subspace.DecodeLast <string>(kvp.Key) !; results[field] = kvp.Value; }) .ConfigureAwait(false); return(results); }
/// <summary>Return one or more fields of an hashset</summary> /// <param name="trans">Transaction that will be used for this request</param> /// <param name="id">Unique identifier of the hashset</param> /// <param name="fields">List of the fields to read</param> /// <returns>Dictionary containing the values of the selected fields, or <see cref="Slice.Empty"/> if that particular field does not exist.</returns> public async Task <IDictionary <string, Slice> > GetAsync(IFdbReadOnlyTransaction trans, IVarTuple id, params string[] fields) { if (trans == null) { throw new ArgumentNullException(nameof(trans)); } if (id == null) { throw new ArgumentNullException(nameof(id)); } if (fields == null) { throw new ArgumentNullException(nameof(fields)); } var keys = TuPack.EncodePrefixedKeys(GetKey(id), fields); var values = await trans.GetValuesAsync(keys).ConfigureAwait(false); Contract.Debug.Assert(values != null && values.Length == fields.Length); var results = new Dictionary <string, Slice>(values.Length, StringComparer.OrdinalIgnoreCase); for (int i = 0; i < fields.Length; i++) { results[fields[i]] = values[i]; } return(results); }
/// <summary>Returns a list of ids matching a specific value</summary> /// <param name="trans"></param> /// <param name="value">Value to lookup</param> /// <param name="reverse"></param> /// <returns>List of document ids matching this value for this particular index (can be empty if no document matches)</returns> public Task <List <TId> > LookupAsync([NotNull] IFdbReadOnlyTransaction trans, TValue value, bool reverse = false) { var query = Lookup(trans, value, reverse); //TODO: limits? paging? ... return(query.ToListAsync()); }
/// <inheritdoc /> public ValueTask <TState> GetState(IFdbReadOnlyTransaction tr) { tr.Cancellation.ThrowIfCancellationRequested(); var scope = EnsureReady(); return(new ValueTask <TState>(scope.State)); }
/// <summary>Reads the values of multiple entries in the map</summary> /// <param name="trans">Transaction used for the operation</param> /// <param name="ids">List of the keys to read</param> /// <returns>Array of results, in the same order as specified in <paramref name="ids"/>.</returns> public async Task <TValue[]> GetValuesAsync([NotNull] IFdbReadOnlyTransaction trans, [NotNull] IEnumerable <TKey> ids) { if (trans == null) { throw new ArgumentNullException(nameof(trans)); } if (ids == null) { throw new ArgumentNullException(nameof(ids)); } var kv = await trans.GetValuesAsync(ids.Select(id => this.Subspace.Keys[id])).ConfigureAwait(false); if (kv.Length == 0) { return(Array.Empty <TValue>()); } var result = new TValue[kv.Length]; var decoder = this.ValueEncoder; for (int i = 0; i < kv.Length; i++) { result[i] = decoder.DecodeValue(kv[i]); } return(result); }
public static async Task <DirectoryKeyResolver> BuildFromDirectoryLayer(IFdbReadOnlyTransaction tr, FdbDirectoryLayer directory) { var location = directory.NodeSubspace.Keys; //HACKHACK: for now, we will simply poke inside the node subspace of the directory layer, which is brittle (if the structure changes in future versions!) // Entries that correspond to subfolders have the form: NodeSubspace.Pack( (parent_prefix, 0, "child_name") ) = child_prefix var keys = await tr.GetRange(location.ToRange()).ToListAsync(); var map = new Dictionary <Slice, string>(Slice.Comparer.Default); foreach (var entry in keys) { var t = location.Unpack(entry.Key); // look for a tuple of size 3 with 0 as the second element... if (t.Count != 3 || t.Get <int>(1) != 0) { continue; } //Slice parent = t.Get<Slice>(0); //TODO: use this to construct the full materialized path of this directory? (would need more than one pass) string name = t.Get <string>(2); map[entry.Value] = name; } return(new DirectoryKeyResolver(map)); }
public static async Task <FdbSystemStatus> GetStatusAsync([NotNull] IFdbReadOnlyTransaction trans) { Contract.NotNull(trans, nameof(trans)); Slice data = await trans.GetAsync(StatusJsonKey).ConfigureAwait(false); if (data.IsNullOrEmpty) { return(null); } string jsonText = data.ToUnicode(); var doc = TinyJsonParser.ParseObject(jsonText); if (doc == null) { return(null); } long rv = 0; if (doc.ContainsKey("cluster")) { rv = await trans.GetReadVersionAsync(); } return(new FdbSystemStatus(doc, rv, jsonText)); }
public static void Annotate(this IFdbReadOnlyTransaction trans, string format, params object?[] args) { if (trans.IsLogged()) { trans.Annotate(string.Format(CultureInfo.InvariantCulture, format, args)); } }
/// <summary>Return the keys that are in the first range, but not in the others</summary> /// <typeparam name="TKey">Type of the keys used for the comparison</typeparam> /// <typeparam name="TResult">Type of the results returned by the query</typeparam> /// <param name="trans">Transaction used by the operation</param> /// <param name="ranges">List of at least one key ranges</param> /// <param name="keySelector">Lambda called to extract the keys used by the sort</param> /// <param name="resultSelector">Lambda called to extract the values returned by the query</param> /// <param name="keyComparer">Instance used to compare the keys returned by <paramref name="keySelector"/></param> /// <returns>Async query that returns only the results that are in the first range, and not in any other range.</returns> public static IFdbAsyncEnumerable <TResult> Except <TKey, TResult>(this IFdbReadOnlyTransaction trans, IEnumerable <FdbKeyRange> ranges, Func <KeyValuePair <Slice, Slice>, TKey> keySelector, Func <KeyValuePair <Slice, Slice>, TResult> resultSelector, IComparer <TKey> keyComparer = null) { if (ranges == null) { throw new ArgumentNullException("ranges"); } return(Except <TKey, TResult>(trans, ranges.Select(r => FdbKeySelectorPair.Create(r)), keySelector, resultSelector, keyComparer)); }
public ResultIterator([NotNull] FdbRangeQuery <T> query, IFdbReadOnlyTransaction transaction, [NotNull] Func <KeyValuePair <Slice, Slice>, T> transform) { Contract.Requires(query != null && transform != null); m_query = query; m_transaction = transaction ?? query.Transaction; m_resultTransform = transform; }
/// <summary> /// Get the size (in bytes) of the blob. /// </summary> /// <returns>Return null if the blob does not exists, 0 if is empty, or the size in bytes</returns> public Task <long?> GetSizeAsync(IFdbReadOnlyTransaction trans) { if (trans == null) { throw new ArgumentNullException(nameof(trans)); } return(GetSizeInternalAsync(trans)); }
protected FdbReadOnlyTransactionFilter(IFdbReadOnlyTransaction transaction) { if (transaction == null) { throw new ArgumentNullException("transaction"); } m_transaction = transaction; }
/// <summary>Returns the list of all the subdirectories of the current directory.</summary> public Task <List <string> > ListAsync(IFdbReadOnlyTransaction trans) { if (trans == null) { throw new ArgumentNullException("trans"); } return(this.DirectoryLayer.ListInternalAsync(trans, this.RelativeLocation, throwIfMissing: true)); }
public IAsyncEnumerable <TId> LookupLessThan(IFdbReadOnlyTransaction trans, TValue value, bool orEqual, bool reverse = false) { if (trans == null) { throw new ArgumentNullException(nameof(trans)); } return(AsyncEnumerable.Defer <TId, FdbRangeQuery <TId> >((_) => CreateLookupLessThanQuery(trans, value, orEqual, reverse))); }
/// <summary>Returns the list of all the subdirectories of the current directory, it it exists.</summary> public Task <List <string> > TryListAsync(IFdbReadOnlyTransaction trans, IEnumerable <string> path) { if (trans == null) { throw new ArgumentNullException("trans"); } return(this.DirectoryLayer.ListInternalAsync(trans, ToRelativePath(path), throwIfMissing: false)); }
public ValueTask <TState> GetState(IFdbReadOnlyTransaction tr) { return(new ValueTask <TState>( tr.Cancellation.IsCancellationRequested ? Task.FromCanceled <TState>(tr.Cancellation) : m_disposed?Task.FromException <TState>(ThrowHelper.ObjectDisposedException(this)) : Task.FromException <TState>(this.Error) )); }
/// <summary>Get the number of items in the Vector. This number includes the sparsely represented items.</summary> public Task <long> SizeAsync(IFdbReadOnlyTransaction tr) { if (tr == null) { throw new ArgumentNullException(nameof(tr)); } return(ComputeSizeAsync(tr)); }
/// <summary>Load a document from the collection</summary> /// <param name="trans"></param> /// <param name="id"></param> /// <returns></returns> public async Task<TDocument> LoadAsync(IFdbReadOnlyTransaction trans, TId id) { if (trans == null) throw new ArgumentNullException(nameof(trans)); if (id == null) throw new ArgumentNullException(nameof(id)); // only for ref types var parts = await LoadPartsAsync(trans, id).ConfigureAwait(false); return DecodeParts(parts); }
protected virtual Task<List<Slice>> LoadPartsAsync(IFdbReadOnlyTransaction trans, TId id) { var key = this.Subspace.Keys.EncodePartial(id); return trans .GetRange(KeyRange.StartsWith(key)) //TODO: options ? .Select(kvp => kvp.Value) .ToListAsync(); }
/// <summary>Load multiple documents from the collection</summary> /// <param name="trans"></param> /// <param name="ids"></param> /// <returns></returns> public async Task<List<TDocument>> LoadMultipleAsync(IFdbReadOnlyTransaction trans, IEnumerable<TId> ids) { if (trans == null) throw new ArgumentNullException(nameof(trans)); if (ids == null) throw new ArgumentNullException(nameof(ids)); var results = await Task.WhenAll(ids.Select(id => LoadPartsAsync(trans, id))); return results.Select(parts => DecodeParts(parts)).ToList(); }
/// <summary>Get the value of the counter with snapshot isolation (no transaction conflicts).</summary> public Task <long> GetSnapshot(IFdbReadOnlyTransaction trans) { if (trans == null) { throw new ArgumentNullException("trans"); } return(GetTransactional(trans.Snapshot)); }
private static async Task PrintRankedSet(FdbRankedSet rs, IFdbReadOnlyTransaction tr) { var sb = new StringBuilder(); for (int l = 0; l < 6; l++) { sb.AppendFormat("Level {0}:\r\n", l); await tr.GetRange(rs.Subspace.Partition(l).ToRange()).ForEachAsync((kvp) => { sb.AppendFormat("\t{0} = {1}\r\n", rs.Subspace.Unpack(kvp.Key), kvp.Value.ToInt64()); }); } Console.WriteLine(sb.ToString()); }
Task<List<string>> IFdbDirectory.TryListAsync(IFdbReadOnlyTransaction trans, IEnumerable<string> path) { return m_directory.TryListAsync(trans, path); }
/// <summary>Finds a node subspace, given its path, by walking the tree from the root.</summary> /// <returns>Node if it was found, or null</returns> private async Task<Node> FindAsync(IFdbReadOnlyTransaction tr, IFdbTuple path) { Contract.Requires(tr != null && path != null); // look for the node by traversing from the root down. Stop when crossing a partition... var n = this.RootNode; int i = 0; Slice layer = Slice.Nil; while (i < path.Count) { if (FdbDirectoryLayer.AnnotateTransactions) tr.Annotate("Looking for child {0} under node {1}...", path.Get<string>(i), n.Key); n = NodeWithPrefix(await tr.GetAsync(GetSubDirKey(n, path.Get<string>(i))).ConfigureAwait(false)); if (n == null) { return new Node(null, path.Substring(0, i + 1), path, Slice.Empty); } if (FdbDirectoryLayer.AnnotateTransactions) tr.Annotate("Reading Layer value for subfolder {0} found at {1}", path, n.Key); layer = await tr.GetAsync(n.Pack(LayerSuffix)).ConfigureAwait(false); if (layer == FdbDirectoryPartition.LayerId) { // stop when reaching a partition return new Node(n, path.Substring(0, i + 1), path, FdbDirectoryPartition.LayerId); } ++i; } return new Node(n, path, path, layer); }
/// <summary>Return the list the names of all fields of an hashset</summary> /// <param name="trans">Transaction that will be used for this request</param> /// <param name="id">Unique identifier of the hashset</param> /// <returns>List of all fields. If the list is empty, the hashset does not exist</returns> public Task<List<string>> GetKeys(IFdbReadOnlyTransaction trans, IFdbTuple id, CancellationToken cancellationToken = default(CancellationToken)) { //note: As of Beta2, FDB does not have a fdb_get_range that only return the keys. That means that we will have to also read the values from the db, in order to just get the names of the fields :( //TODO: find a way to optimize this ? if (trans == null) throw new ArgumentNullException("trans"); if (id == null) throw new ArgumentNullException("id"); var prefix = GetKey(id); var results = new Dictionary<string, Slice>(StringComparer.OrdinalIgnoreCase); return trans .GetRange(FdbKeyRange.StartsWith(prefix)) .Select((kvp) => ParseFieldKey(FdbTuple.Unpack(kvp.Key))) .ToListAsync(cancellationToken); }
/// <summary>Attempts to open the directory with the given <paramref name="name"/>.</summary> public static Task<FdbDirectorySubspace> TryOpenAsync(this IFdbDirectory directory, IFdbReadOnlyTransaction trans, string name, Slice layer) { if (directory == null) throw new ArgumentNullException("directory"); if (trans == null) throw new ArgumentNullException("trans"); if (name == null) throw new ArgumentNullException("name"); return directory.TryOpenAsync(trans, new[] { name }, layer); }
/// <summary>Returns the list of subdirectories of the sub-directory with the given <paramref name="name"/>, if it exists</summary> public static Task<List<string>> TryListAsync(this IFdbDirectory directory, IFdbReadOnlyTransaction trans, string name) { if (directory == null) throw new ArgumentNullException("directory"); if (trans == null) throw new ArgumentNullException("trans"); if (name == null) throw new ArgumentNullException("name"); return directory.TryListAsync(trans, new[] { name }); }
public Task<List<string>> TryListAsync(IFdbReadOnlyTransaction trans) { if (trans == null) throw new ArgumentNullException("trans"); return ListInternalAsync(trans, FdbTuple.Empty, throwIfMissing: false); }
/// <summary>Returns the list of all the subdirectories of a sub-directory, it it exists.</summary> public Task<List<string>> TryListAsync(IFdbReadOnlyTransaction trans) { if (trans == null) throw new ArgumentNullException("trans"); return this.DirectoryLayer.ListInternalAsync(trans, this.RelativeLocation, throwIfMissing: false); }
/// <summary>Checks if a sub-directory exists</summary> /// <returns>Returns true if the directory exists, otherwise false.</returns> public Task<bool> ExistsAsync(IFdbReadOnlyTransaction trans, IEnumerable<string> path) { if (trans == null) throw new ArgumentNullException("trans"); // If path is empty, we are checking ourselves! var location = FdbDirectoryLayer.ParsePath(path, "path"); if (location.Count == 0) { return ExistsAsync(trans); } return this.DirectoryLayer.ExistsInternalAsync(trans, ToRelativePath(location)); }
/// <summary>Checks if this directory exists</summary> /// <returns>Returns true if the directory exists, otherwise false.</returns> public Task<bool> ExistsAsync(IFdbReadOnlyTransaction trans) { if (trans == null) throw new ArgumentNullException("trans"); // if 'this' is a Directory Partition, we need to remove it from the parent DL ! var directoryLayer = GetLayerForPath(FdbTuple.Empty); return directoryLayer.ExistsInternalAsync(trans, this.RelativeLocation); }
/// <summary>Opens a subdirectory with the given <paramref name="path"/>. /// An exception is thrown if the subdirectory if a layer is specified and a different layer was specified when the subdirectory was created. /// </summary> /// <param name="trans">Transaction to use for the operation</param> /// <param name="path">Relative path of the subdirectory to open</param> /// <param name="layer">If specified, the opened directory must have the same layer id.</param> /// <returns>Returns the directory if it exists, or null if it was not found</returns> public Task<FdbDirectorySubspace> TryOpenAsync(IFdbReadOnlyTransaction trans, IEnumerable<string> path, Slice layer = default(Slice)) { if (trans == null) throw new ArgumentNullException("trans"); if (path == null) throw new ArgumentNullException("path"); return this.DirectoryLayer.CreateOrOpenInternalAsync(trans, null, ToRelativePath(path), layer, prefix: Slice.Nil, allowCreate: false, allowOpen: true, throwOnError: false); }
/// <summary>Checks if a directory already exists</summary> /// <param name="trans">Transaction to use for the operation</param> /// <param name="path">Path of the directory to remove (including any subdirectories)</param> /// <returns>Returns true if the directory exists, otherwise false.</returns> public Task<bool> ExistsAsync(IFdbReadOnlyTransaction trans, IEnumerable<string> path) { if (trans == null) throw new ArgumentNullException("trans"); if (path == null) throw new ArgumentNullException("path"); // no reason to disallow checking for the root directory (could be used to check if a directory layer is initialized?) var location = ParsePath(path); if (location.Count == 0) return Task.FromResult(true); return ExistsInternalAsync(trans, location); }
/// <summary>Returns the list of subdirectories of directory at <paramref name="path"/></summary> /// <param name="trans">Transaction to use for the operation</param> /// <param name="path">Path of the directory to list</param> public Task<List<string>> ListAsync(IFdbReadOnlyTransaction trans, IEnumerable<string> path) { if (trans == null) throw new ArgumentNullException("trans"); return ListInternalAsync(trans, ParsePath(path), throwIfMissing: true); }
/// <summary>Get the value of the counter. /// Not recommended for use with read/write transactions when the counter is being frequently updated (conflicts will be very likely). /// </summary> public async Task<long> GetTransactional(IFdbReadOnlyTransaction trans) { if (trans == null) throw new ArgumentNullException("trans"); long total = 0; await trans .GetRange(this.Subspace.ToRange()) .ForEachAsync((kvp) => { checked { total += this.Encoder.DecodeValue(kvp.Value); } }) .ConfigureAwait(false); return total; }
/// <summary>Returns the list of all the subdirectories of the current directory, it it exists.</summary> public Task<List<string>> TryListAsync(IFdbReadOnlyTransaction trans, IEnumerable<string> path) { if (trans == null) throw new ArgumentNullException("trans"); return this.DirectoryLayer.ListInternalAsync(trans, ToRelativePath(path), throwIfMissing: false); }
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); }
private async Task<bool> IsPrefixFree(IFdbReadOnlyTransaction tr, Slice prefix) { Contract.Requires(tr != null); // Returns true if the given prefix does not "intersect" any currently // allocated prefix (including the root node). This means that it neither // contains any other prefix nor is contained by any other prefix. if (prefix.IsNullOrEmpty) return false; if (await NodeContainingKey(tr, prefix).ConfigureAwait(false) != null) return false; return await tr .GetRange( this.NodeSubspace.Pack(prefix), this.NodeSubspace.Pack(FdbKey.Increment(prefix)) ) .NoneAsync() .ConfigureAwait(false); }
/// <summary> /// Read from the blob, starting at <paramref name="offset"/>, retrieving up to <paramref name="n"/> bytes (fewer then n bytes are returned when the end of the blob is reached). /// </summary> public async Task<Slice> ReadAsync(IFdbReadOnlyTransaction trans, long offset, int n) { if (trans == null) throw new ArgumentNullException("trans"); if (offset < 0) throw new ArgumentNullException("offset", "Offset cannot be less than zero"); long? size = await GetSizeAsync(trans).ConfigureAwait(false); if (size == null) return Slice.Nil; // not found if (offset >= size.Value) return Slice.Empty; // read all chunks matching the segment we need, and copy them in our buffer var buffer = new byte[Math.Min(n, size.Value - offset)]; await trans .GetRange( FdbKeySelector.LastLessOrEqual(DataKey(offset)), FdbKeySelector.FirstGreaterOrEqual(DataKey(offset + n)) ) .ForEachAsync((chunk) => { // get offset of this chunk long chunkOffset = DataKeyOffset(chunk.Key); Slice chunkData = chunk.Value; checked { // intersect chunk bounds with output int delta = (int)(chunkOffset - offset); int start = delta; int end = delta + chunkData.Count; if (start < 0) start = 0; if (end > n) end = n; // compute the relative offsets in the chunk int rStart = start - delta; int rEnd = end - delta; var intersect = chunkData[rStart, rEnd]; if (intersect.IsPresent) { // copy the data that fits intersect.CopyTo(buffer, start); } } }) .ConfigureAwait(false); return new Slice(buffer, 0, buffer.Length); }
protected FdbReadOnlyTransactionFilter(IFdbReadOnlyTransaction transaction) { if (transaction == null) throw new ArgumentNullException("transaction"); m_transaction = transaction; }
/// <summary> /// Get the size (in bytes) of the blob. /// </summary> /// <returns>Return null if the blob does not exists, 0 if is empty, or the size in bytes</returns> public async Task<long?> GetSizeAsync(IFdbReadOnlyTransaction trans) { if (trans == null) throw new ArgumentNullException("trans"); Slice value = await trans.GetAsync(SizeKey()).ConfigureAwait(false); if (value.IsNullOrEmpty) return default(long?); //note: python code stores the size as a string long size = Int64.Parse(value.ToAscii()); if (size < 0) throw new InvalidOperationException("The internal blob size cannot be negative"); return size; }
/// <summary>Returns the list of subdirectories of the current directory.</summary> public static Task<List<string>> ListAsync(this IFdbDirectory directory, IFdbReadOnlyTransaction trans) { if (directory == null) throw new ArgumentNullException("directory"); if (trans == null) throw new ArgumentNullException("trans"); return directory.ListAsync(trans); }
/// <summary>Get the value of the counter with snapshot isolation (no transaction conflicts).</summary> public Task<long> GetSnapshot(IFdbReadOnlyTransaction trans) { if (trans == null) throw new ArgumentNullException("trans"); return GetTransactional(trans.Snapshot); }
private async Task CheckReadVersionAsync(IFdbReadOnlyTransaction trans) { var value = await trans.GetAsync(this.RootNode.Pack(VersionKey)).ConfigureAwait(false); if (!value.IsNullOrEmpty) { CheckVersion(value, false); } }
Task<FdbDirectorySubspace> IFdbDirectory.TryOpenAsync(IFdbReadOnlyTransaction trans, IEnumerable<string> path, Slice layer) { return m_directory.TryOpenAsync(trans, path, layer); }
private Task<long> SlowCountAsync(IFdbReadOnlyTransaction trans, int level, Slice beginKey, Slice endKey) { if (level == -1) { return Task.FromResult<long>(beginKey.IsPresent ? 1 : 0); } return trans .GetRange(this.Subspace.Pack(level, beginKey), this.Subspace.Pack(level, endKey)) .Select(kv => DecodeCount(kv.Value)) .SumAsync(); }
Task<bool> IFdbDirectory.ExistsAsync(IFdbReadOnlyTransaction trans, IEnumerable<string> path) { return m_directory.ExistsAsync(trans, path); }
/// <summary>Returns the list of names and nodes of all children of the specified node</summary> private IFdbAsyncEnumerable<KeyValuePair<string, FdbSubspace>> SubdirNamesAndNodes(IFdbReadOnlyTransaction tr, FdbSubspace node) { Contract.Requires(tr != null && node != null); var sd = node.Partition(SUBDIRS); return tr .GetRange(sd.ToRange()) .Select(kvp => new KeyValuePair<string, FdbSubspace>( sd.UnpackSingle<string>(kvp.Key), NodeWithPrefix(kvp.Value) )); }
private async Task<FdbSubspace> NodeContainingKey(IFdbReadOnlyTransaction tr, Slice key) { Contract.Requires(tr != null); // Right now this is only used for _is_prefix_free(), but if we add // parent pointers to directory nodes, it could also be used to find a // path based on a key. if (this.NodeSubspace.Contains(key)) return this.RootNode; var kvp = await tr .GetRange( this.NodeSubspace.ToRange().Begin, this.NodeSubspace.Pack(key) + FdbKey.MinValue ) .LastOrDefaultAsync() .ConfigureAwait(false); if (kvp.Key.HasValue) { var prevPrefix = this.NodeSubspace.UnpackFirst<Slice>(kvp.Key); if (key.StartsWith(prevPrefix)) { return NodeWithPrefix(prevPrefix); } } return null; }