コード例 #1
0
            /// <summary>Pop the next item from the queue. Cannot be composed with other functions in a single transaction.</summary>
            public async Task <(T Value, bool HasValue)> PopAsync(IFdbTransaction tr)
            {
                Contract.NotNull(tr, nameof(tr));
#if DEBUG
                tr.Annotate("Pop()");
#endif

                var first = await tr.GetRangeAsync(this.Subspace.ToRange(), SingleOptions);

                if (first.IsEmpty)
                {
#if DEBUG
                    tr.Annotate("Got nothing");
#endif
                    return(default);
コード例 #2
0
        private void ClearTask(IFdbTransaction tr, Slice taskId)
        {
            tr.Annotate("Deleting task {0:P}", taskId);

            // clear all metadata about the task
            tr.ClearRange(KeyRange.StartsWith(this.TaskStore.Keys.Encode(taskId)));
            // decrement pending number of tasks
            this.Counters.Decrement(tr, COUNTER_PENDING_TASKS);
        }
コード例 #3
0
        private void ClearTask(IFdbTransaction tr, Slice taskId)
        {
            tr.Annotate("Deleting task {0}", taskId.ToAsciiOrHexaString());

            // clear all metadata about the task
            tr.ClearRange(FdbKeyRange.StartsWith(this.TaskStore.Pack(taskId)));
            // decrement pending number of tasks
            this.Counters.Decrement(tr, COUNTER_PENDING_TASKS);
        }
コード例 #4
0
        /// <summary>Push a single item onto the queue.</summary>
        public async Task PushAsync([NotNull] IFdbTransaction trans, T value)
        {
            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }

#if DEBUG
            trans.Annotate("Push({0})", value);
#endif

            long index = await GetNextIndexAsync(trans.Snapshot, this.QueueItem).ConfigureAwait(false);

#if DEBUG
            trans.Annotate("Index = {0}", index);
#endif

            await PushAtAsync(trans, value, index).ConfigureAwait(false);
        }
コード例 #5
0
            /// <summary>Push a single item onto the queue.</summary>
            public void Push(IFdbTransaction tr, T value)
            {
                Contract.NotNull(tr, nameof(tr));

#if DEBUG
                tr.Annotate("Push({0})", value);
#endif

                //BUGBUG: can be called multiple times per transaction, so need a unique stamp _per_ transaction!!
                tr.SetVersionStampedKey(this.Subspace[tr.CreateUniqueVersionStamp()], this.Encoder.EncodeValue(value));
            }
コード例 #6
0
        private void StoreTask(IFdbTransaction tr, Slice taskId, DateTime scheduledUtc, Slice taskBody)
        {
            tr.Annotate("Writing task {0:P}", taskId);

            var prefix = this.TaskStore.Partition.ByKey(taskId);

            // store task body and timestamp
            tr.Set(prefix.GetPrefix(), taskBody);
            tr.Set(prefix.Keys.Encode(TASK_META_SCHEDULED), Slice.FromInt64(scheduledUtc.Ticks));
            // increment total and pending number of tasks
            this.Counters.Increment(tr, COUNTER_TOTAL_TASKS);
            this.Counters.Increment(tr, COUNTER_PENDING_TASKS);
        }
コード例 #7
0
        private async Task <Optional <T> > PopSimpleAsync([NotNull] IFdbTransaction tr)
        {
#if DEBUG
            tr.Annotate("PopSimple()");
#endif

            var firstItem = await GetFirstItemAsync(tr).ConfigureAwait(false);

            if (firstItem.Key.IsNull)
            {
                return(default(Optional <T>));
            }

            tr.Clear(firstItem.Key);
            return(this.Encoder.DecodeValue(firstItem.Value));
        }
コード例 #8
0
		private void ClearTask(IFdbTransaction tr, Slice taskId)
		{
			tr.Annotate("Deleting task {0}", taskId.ToAsciiOrHexaString());

			// clear all metadata about the task
			tr.ClearRange(FdbKeyRange.StartsWith(this.TaskStore.Pack(taskId)));
			// decrement pending number of tasks
			this.Counters.Decrement(tr, COUNTER_PENDING_TASKS);
		}
コード例 #9
0
		private void StoreTask(IFdbTransaction tr, Slice taskId, DateTime scheduledUtc, Slice taskBody)
		{
			tr.Annotate("Writing task {0}", taskId.ToAsciiOrHexaString());

			var prefix = this.TaskStore.Partition(taskId);

			// store task body and timestamp
			tr.Set(prefix.Key, taskBody);
			tr.Set(prefix.Pack(TASK_META_SCHEDULED), Slice.FromInt64(scheduledUtc.Ticks));
			// increment total and pending number of tasks
			this.Counters.Increment(tr, COUNTER_TOTAL_TASKS);
			this.Counters.Increment(tr, COUNTER_PENDING_TASKS);
		}
コード例 #10
0
        /// <summary>Returns a 64-bit integer that
        /// 1) has never and will never be returned by another call to this
        ///    method on the same subspace
        /// 2) is nearly as short as possible given the above
        /// </summary>
        public async Task <long> AllocateAsync([NotNull] IFdbTransaction trans)
        {
            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }

            // find the current window size, by reading the last entry in the 'counters' subspace
            long start = 0, count = 0;
            var  kv = await trans
                      .Snapshot
                      .GetRange(this.Counters.Keys.ToRange())
                      .LastOrDefaultAsync();

            if (kv.Key.IsPresent)
            {
                start = this.Counters.Keys.Decode <long>(kv.Key);
                count = kv.Value.ToInt64();
            }

            // check if the window is full
            int window = GetWindowSize(start);

            if ((count + 1) * 2 >= window)
            {             // advance the window
                if (FdbDirectoryLayer.AnnotateTransactions)
                {
                    trans.Annotate("Advance allocator window size to {0} starting at {1}", window, start + window);
                }
                trans.ClearRange(this.Counters.GetPrefix(), this.Counters.Keys.Encode(start) + FdbKey.MinValue);
                start += window;
                count  = 0;
                trans.ClearRange(this.Recent.GetPrefix(), this.Recent.Keys.Encode(start));
            }

            // Increment the allocation count for the current window
            trans.AtomicAdd64(this.Counters.Keys.Encode(start), 1);

            // As of the snapshot being read from, the window is less than half
            // full, so this should be expected to take 2 tries.  Under high
            // contention (and when the window advances), there is an additional
            // subsequent risk of conflict for this transaction.
            while (true)
            {
                // Find a random free slot in the current window...
                long candidate;
                lock (m_rnd)
                {
                    candidate = start + m_rnd.Next(window);
                }

                // test if the key is used
                var key   = this.Recent.Keys.Encode(candidate);
                var value = await trans.GetAsync(key).ConfigureAwait(false);

                if (value.IsNull)
                {                 // free slot
                    // mark as used
                    trans.Set(key, Slice.Empty);
                    if (FdbDirectoryLayer.AnnotateTransactions)
                    {
                        trans.Annotate("Allocated prefix {0} from window [{1}..{2}] ({3} used)", candidate, start, start + window - 1, count + 1);
                    }
                    return(candidate);
                }

                // no luck this time, try again...
            }
        }
コード例 #11
0
        public static async Task <long> AllocateAsync(IFdbTransaction trans, ITypedKeySubspace <int, long> subspace, Random rng)
        {
            Contract.NotNull(trans);

            // find the current window size, by reading the last entry in the 'counters' subspace
            long start = 0, count = 0;
            var  kv = await trans
                      .Snapshot
                      .GetRange(subspace.EncodePartialRange(COUNTERS))
                      .LastOrDefaultAsync();

            if (kv.Key.Count != 0)
            {
                start = subspace.DecodeLast(kv.Key);
                count = kv.Value.ToInt64();
            }

            // check if the window is full
            int window = GetWindowSize(start);

            if ((count + 1) * 2 >= window)
            {             // advance the window
                if (FdbDirectoryLayer.AnnotateTransactions)
                {
                    trans.Annotate("Advance allocator window size to {0} starting at {1}", window, start + window);
                }
                trans.ClearRange(subspace[COUNTERS, 0], subspace[COUNTERS, start + 1]);
                start += window;
                count  = 0;
                trans.ClearRange(subspace[RECENT, 0], subspace[RECENT, start]);
            }

            // Increment the allocation count for the current window
            trans.AtomicAdd64(subspace[COUNTERS, start], 1);

            // As of the snapshot being read from, the window is less than half
            // full, so this should be expected to take 2 tries.  Under high
            // contention (and when the window advances), there is an additional
            // subsequent risk of conflict for this transaction.
            while (true)
            {
                // Find a random free slot in the current window...
                long candidate;
                lock (rng)
                {
                    candidate = start + rng.Next(window);
                }

                // test if the key is used
                var key   = subspace[RECENT, candidate];
                var value = await trans.GetAsync(key).ConfigureAwait(false);

                if (value.IsNull)
                {                 // free slot
                    // mark as used
                    trans.Set(key, Slice.Empty);
                    if (FdbDirectoryLayer.AnnotateTransactions)
                    {
                        trans.Annotate("Allocated prefix {0} from window [{1}..{2}] ({3} used)", candidate, start, start + window - 1, count + 1);
                    }
                    return(candidate);
                }

                // no luck this time, try again...
            }
        }
コード例 #12
0
		/// <summary>Resursively remove a node (including the content), all its children</summary>
		private async Task RemoveRecursive(IFdbTransaction tr, FdbSubspace node)
		{
			Contract.Requires(tr != null && node != null);

			//note: we could use Task.WhenAll to remove the children, but there is a risk of task explosion if the subtree is very large...
			await SubdirNamesAndNodes(tr, node).ForEachAsync((kvp) => RemoveRecursive(tr, kvp.Value)).ConfigureAwait(false);

			// remove ALL the contents
			if (FdbDirectoryLayer.AnnotateTransactions) tr.Annotate("Removing all content located under {0}", node.Key);
			tr.ClearRange(FdbKeyRange.StartsWith(ContentsOfNode(node, FdbTuple.Empty, Slice.Empty).Key));
			// and all the metadata for this folder
			if (FdbDirectoryLayer.AnnotateTransactions) tr.Annotate("Removing all metadata for folder under {0}", node.Key);
			tr.ClearRange(node.ToRange());
		}
コード例 #13
0
		/// <summary>Remove an existing node from its parents</summary>
		/// <returns>True if the parent node was found, otherwise false</returns>
		private async Task<bool> RemoveFromParent(IFdbTransaction tr, IFdbTuple path)
		{
			Contract.Requires(tr != null && path != null);

			var parent = await FindAsync(tr, path.Substring(0, path.Count - 1)).ConfigureAwait(false);
			if (parent.Exists)
			{
				if (FdbDirectoryLayer.AnnotateTransactions) tr.Annotate("Removing path {0} from its parent folder at {1}", path, parent.Subspace.Key);
				tr.Clear(GetSubDirKey(parent.Subspace, path.Get<string>(-1)));
				return true;
			}
			return false;
		}
コード例 #14
0
		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);
		}