Example #1
0
 /// <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;
		}
Example #6
0
        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();
		}
Example #8
0
		/// <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);
Example #10
0
        /// <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));
        }
Example #11
0
        /// <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);
        }
Example #12
0
        /// <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);
        }
Example #13
0
        /// <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);
        }
Example #17
0
            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));
            }
Example #19
0
 public static void Annotate(this IFdbReadOnlyTransaction trans, string format, params object?[] args)
 {
     if (trans.IsLogged())
     {
         trans.Annotate(string.Format(CultureInfo.InvariantCulture, format, args));
     }
 }
Example #20
0
 /// <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));
 }
Example #21
0
            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;
            }
Example #22
0
 /// <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));
 }
Example #25
0
 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)
                ));
 }
Example #28
0
            /// <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);
		}
Example #46
0
		/// <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);
		}
Example #54
0
		/// <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;
		}