Exemple #1
0
        /// <summary>Decrements the count of an (index, value) pair in the multimap, and optionally removes it if the count reaches zero.</summary>
        /// <param name="trans">Transaction used for the operation</param>
        /// <param name="key">Key of the entry</param>
        /// <param name="value">Value for the <paramref name="key"/> to decrement</param>
        /// <remarks>If the updated count reaches zero or less, and AllowNegativeValues is not set, the key will be cleared from the map.</remarks>
        public async Task SubtractAsync([NotNull] IFdbTransaction trans, TKey key, TValue value)
        {
            if (trans == null)
            {
                throw new ArgumentNullException("trans");
            }

            Slice k = this.Location.Keys.Encode(key, value);

            if (this.AllowNegativeValues)
            {
                trans.AtomicAdd(k, MinusOne);
                // note: it's faster, but we will end up with counts less than or equal to 0
                // If 'k' does not already exist, its count will be set to -1
            }
            else
            {
                Slice v = await trans.GetAsync(k).ConfigureAwait(false);

                if (this.AllowNegativeValues || v.ToInt64() > 1)                 //note: Slice.Nil.ToInt64() will return 0
                {
                    trans.AtomicAdd(k, MinusOne);
                    //note: since we already read 'k', the AtomicAdd will be optimized into the equivalent of Set(k, v - 1) by the client, unless RYW has been disabled on the transaction
                    //TODO: if AtomicMax ever gets implemented, we could use it to truncate the values to 0
                }
                else
                {
                    trans.Clear(k);
                }
            }
        }
Exemple #2
0
		/// <summary>Increments the count of an (index, value) pair in the multimap.</summary>
		/// <param name="trans">Transaction used for the operation</param>
		/// <param name="key">Key of the entry</param>
		/// <param name="value">Value for the <paramref name="key"/> to increment</param>
		/// <remarks>If the (index, value) pair does not exist, its value is considered to be 0</remarks>
		/// <exception cref="System.ArgumentNullException">If <paramref name="trans"/> is null.</exception>
		public Task AddAsync([NotNull] IFdbTransaction trans, TKey key, TValue value)
		{
			//note: this method does not need to be async, but subtract is, so it's better if both methods have the same shape.
			if (trans == null) throw new ArgumentNullException(nameof(trans));

			trans.AtomicAdd(this.Subspace.Keys[key, value], PlusOne);
			return Task.CompletedTask;
		}
		private async Task Scenario6(IFdbTransaction tr)
		{
			var location = FdbSubspace.Create(Slice.FromAscii("TEST"));

			tr.AtomicAdd(location.Pack("ATOMIC"), Slice.FromFixed32(0x55555555));

			var x = await tr.GetAsync(location.Pack("ATOMIC"));
			Console.WriteLine(x.ToInt32().ToString("x"));
		}
        private async Task Scenario6(IFdbTransaction tr)
        {
            var location = FdbSubspace.Create(Slice.FromAscii("TEST"));

            tr.AtomicAdd(location.Pack("ATOMIC"), Slice.FromFixed32(0x55555555));

            var x = await tr.GetAsync(location.Pack("ATOMIC"));

            Console.WriteLine(x.ToInt32().ToString("x"));
        }
Exemple #5
0
        /// <summary>Increments the count of an (index, value) pair in the multimap.</summary>
        /// <param name="trans">Transaction used for the operation</param>
        /// <param name="key">Key of the entry</param>
        /// <param name="value">Value for the <paramref name="key"/> to increment</param>
        /// <remarks>If the (index, value) pair does not exist, its value is considered to be 0</remarks>
        /// <exception cref="System.ArgumentNullException">If <paramref name="trans"/> is null.</exception>
        public Task AddAsync([NotNull] IFdbTransaction trans, TKey key, TValue value)
        {
            //note: this method does not need to be async, but subtract is, so it's better if both methods have the same shape.
            if (trans == null)
            {
                throw new ArgumentNullException("trans");
            }

            trans.AtomicAdd(this.Location.Keys.Encode(key, value), PlusOne);
            return(FoundationDB.Async.TaskHelpers.CompletedTask);
        }
        private Task Scenario3(IFdbTransaction tr)
        {
            var location = FdbSubspace.Create(Slice.FromAscii("TEST"));

            tr.Set(location.Key + (byte)'a', Slice.FromAscii("A"));
            tr.AtomicAdd(location.Key + (byte)'k', Slice.FromFixed32(1));
            tr.Set(location.Key + (byte)'z', Slice.FromAscii("C"));
            tr.ClearRange(location.Key + (byte)'a', location.Key + (byte)'k');
            tr.ClearRange(location.Key + (byte)'k', location.Key + (byte)'z');
            return(Task.FromResult <object>(null));
        }
		private Task Scenario3(IFdbTransaction tr)
		{
			var location = FdbSubspace.Create(Slice.FromAscii("TEST"));

			tr.Set(location.Key + (byte)'a', Slice.FromAscii("A"));
			tr.AtomicAdd(location.Key + (byte)'k', Slice.FromFixed32(1));
			tr.Set(location.Key + (byte)'z', Slice.FromAscii("C"));
			tr.ClearRange(location.Key + (byte)'a', location.Key + (byte)'k');
			tr.ClearRange(location.Key + (byte)'k', location.Key + (byte)'z');
			return Task.FromResult<object>(null);
		}
        /// <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("transaction");
            }
            if (counterKey == null)
            {
                throw new ArgumentNullException("counterKey");
            }

            //REVIEW: we could no-op if value == 0 but this may change conflict behaviour for other transactions...
            Slice param = value == 1 ? PlusOne : value == -1 ? MinusOne : Slice.FromFixed64(value);

            transaction.AtomicAdd(this.Location.Keys.Encode(counterKey), param);
        }
        public async Task InsertAsync([NotNull] IFdbTransaction trans, Slice key)
        {
            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }

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

            int keyHash = key.GetHashCode();             //TODO: proper hash function?

            //Console.WriteLine("Inserting " + key + " with hash " + keyHash.ToString("x"));
            for (int level = 0; level < MAX_LEVELS; level++)
            {
                var prevKey = await GetPreviousNodeAsync(trans, level, key);

                if ((keyHash & ((1 << (level * LEVEL_FAN_POW)) - 1)) != 0)
                {
                    //Console.WriteLine("> [" + level + "] Incrementing previous key: " + FdbKey.Dump(prevKey));
                    trans.AtomicAdd(this.Subspace.Keys.Encode(level, prevKey), EncodeCount(1));
                }
                else
                {
                    //Console.WriteLine("> [" + level + "] inserting and updating previous key: " + FdbKey.Dump(prevKey));
                    // Insert into this level by looking at the count of the previous
                    // key in the level and recounting the next lower level to correct
                    // the counts
                    var prevCount    = DecodeCount(await trans.GetAsync(this.Subspace.Keys.Encode(level, prevKey)).ConfigureAwait(false));
                    var newPrevCount = await SlowCountAsync(trans, level - 1, prevKey, key);

                    var count = checked ((prevCount - newPrevCount) + 1);

                    // print "insert", key, "level", level, "count", count,
                    // "splits", prevKey, "oldC", prevCount, "newC", newPrevCount
                    trans.Set(this.Subspace.Keys.Encode(level, prevKey), EncodeCount(newPrevCount));
                    trans.Set(this.Subspace.Keys.Encode(level, key), EncodeCount(count));
                }
            }
        }
        public async Task EraseAsync([NotNull] 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.Keys.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.AtomicAdd(this.Subspace.Keys.Encode(level, prevKey), EncodeCount(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("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.Key, this.Counters.Keys.Encode(start) + FdbKey.MinValue);
                start += window;
                count  = 0;
                trans.ClearRange(this.Recent.Key, this.Recent.Keys.Encode(start));
            }

            // Increment the allocation count for the current window
            trans.AtomicAdd(this.Counters.Keys.Encode(start), Slice.FromFixed64(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...
            }
        }
		/// <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(IFdbTransaction 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.ToRange())
				.LastOrDefaultAsync();

			if (kv.Key.IsPresent)
			{
				start = this.Counters.UnpackSingle<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
				trans.ClearRange(this.Counters.Key, this.Counters.Pack(start) + FdbKey.MinValue);
				start += window;
				trans.ClearRange(this.Recent.Key, this.Recent.Pack(start));
			}

			// Increment the allocation count for the current window
			trans.AtomicAdd(this.Counters.Pack(start), Slice.FromFixed64(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.Pack(candidate);
				var value = await trans.GetAsync(key).ConfigureAwait(false);

				if (value.IsNull)
				{ // free slot

					// mark as used
					trans.Set(key, Slice.Empty);
					return candidate;
				}

				// no luck this time, try again...
			}
		}