Example #1
0
        /// <summary>Add a value to a counter in one atomic operation</summary>
        /// <param name="transaction"></param>
        /// <param name="counterKey">Key of the counter, relative to the list's subspace</param>
        /// <param name="value">Value that will be added</param>
        /// <remarks>This operation will not cause the current transaction to conflict. It may create conflicts for transactions that would read the value of the counter.</remarks>
        public void Add([NotNull] IFdbTransaction transaction, [NotNull] TKey counterKey, long value)
        {
            if (transaction == null)
            {
                throw new ArgumentNullException(nameof(transaction));
            }
            if (counterKey == null)
            {
                throw new ArgumentNullException(nameof(counterKey));
            }

            //REVIEW: we could no-op if value == 0 but this may change conflict behaviour for other transactions...
            transaction.AtomicAdd64(this.Location.Keys[counterKey], value);
        }
            public async Task EraseAsync(IFdbTransaction trans, Slice key)
            {
                if (trans == null)
                {
                    throw new ArgumentNullException(nameof(trans));
                }

                if (!(await ContainsAsync(trans, key).ConfigureAwait(false)))
                {
                    return;
                }

                for (int level = 0; level < MAX_LEVELS; level++)
                {
                    // This could be optimized with hash
                    var k = this.Subspace.Encode(level, key);
                    var c = await trans.GetAsync(k).ConfigureAwait(false);

                    if (c.HasValue)
                    {
                        trans.Clear(k);
                    }
                    if (level == 0)
                    {
                        continue;
                    }

                    var prevKey = await GetPreviousNodeAsync(trans, level, key);

                    Contract.Assert(prevKey != key);
                    long countChange = -1;
                    if (c.HasValue)
                    {
                        countChange += DecodeCount(c);
                    }

                    trans.AtomicAdd64(this.Subspace.Encode(level, prevKey), countChange);
                }
            }
        /// <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...
            }
        }
        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...
            }
        }