Example #1
0
        /// <summary>
        /// Signup a student to a class
        /// </summary>
        public async Task Signup(IFdbTransaction tr, string s, string c)
        {
            var rec = AttendsKey(s, c);

            if ((await tr.GetAsync(rec)).IsPresent)
            {             // already signed up
                return;
            }
            int seatsLeft = Int32.Parse((await tr.GetAsync(ClassKey(c))).ToStringAscii());

            if (seatsLeft <= 0)
            {
                throw new InvalidOperationException("No remaining seats");
            }

            var classes = await tr.GetRange(AttendsKeys(s)).ToListAsync();

            if (classes.Count >= 5)
            {
                throw new InvalidOperationException("Too many classes");
            }

            tr.Set(ClassKey(c), Slice.FromStringAscii((seatsLeft - 1).ToString()));
            tr.Set(rec, Slice.Empty);
        }
Example #2
0
        /// <summary>Insert a new document in the collection</summary>
        public async Task InsertAsync(IFdbTransaction trans, TDocument document)
        {
            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }
            if (document == null)
            {
                throw new ArgumentNullException(nameof(document));
            }

            var id = this.IdSelector(document);

            if (id == null)
            {
                throw new InvalidOperationException("Cannot insert a document with a null identifier");
            }

            // encode the document
            var packed = this.ValueEncoder.EncodeValue(document);

            var subspace = await this.Location.Resolve(trans);

            if (subspace == null)
            {
                throw new InvalidOperationException($"Location '{this.Location}' referenced by Document Collection Layer was not found.");
            }

            // Key Prefix = ...(id,)
            var key = subspace.EncodePartial(id);

            // clear previous value
            trans.ClearRange(KeyRange.StartsWith(key));

            int remaining = packed.Count;

            if (remaining <= this.ChunkSize)
            {             // stored as a single element
                // Key = ...(id,)
                trans.Set(key, packed);
            }
            else
            {             // splits in as many chunks as necessary
                // Key = ...(id, N) where N is the chunk index (0-based)
                int p     = 0;
                int index = 0;
                while (remaining > 0)
                {
                    int sz = Math.Max(remaining, this.ChunkSize);
                    trans.Set(subspace[id, index], packed.Substring(p, sz));
                    ++index;
                    p         += sz;
                    remaining -= sz;
                }
            }
        }
        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);
		}
        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);
        }
        /// <summary>Insert a new document in the collection</summary>
        public void Insert(IFdbTransaction trans, TDocument document)
        {
            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }
            if (document == null)
            {
                throw new ArgumentNullException(nameof(document));
            }

            var id = this.IdSelector(document);

            if (id == null)
            {
                throw new InvalidOperationException("Cannot insert a document with a null identifier");
            }

            // encode the document
            var packed = this.ValueEncoder.EncodeValue(document);

            // Key Prefix = ...(id,)
            var key = this.Location.Partial.Keys.Encode(id);

            // clear previous value
            trans.ClearRange(KeyRange.StartsWith(key));

            int remaining = packed.Count;

            if (remaining <= this.ChunkSize)
            {             // stored as a single element
                // Key = ...(id,)
                trans.Set(key, packed);
            }
            else
            {             // splits in as many chunks as necessary
                // Key = ...(id, N) where N is the chunk index (0-based)
                int p     = 0;
                int index = 0;
                while (remaining > 0)
                {
                    int sz = Math.Max(remaining, this.ChunkSize);
                    trans.Set(this.Location.Keys.Encode(id, index), packed.Substring(p, sz));
                    ++index;
                    p         += sz;
                    remaining -= sz;
                }
            }
        }
        private async Task <bool> TryRemoteSplitPointAsync([NotNull] IFdbTransaction trans, long offset)
        {
            Contract.Requires(trans != null && offset >= 0);

            var b = await GetChunkAtAsync(trans, offset).ConfigureAwait(false);

            if (b.Offset == 0 || b.Key == Slice.Nil)
            {
                return(false);                                                 // in sparse region, or at beginning
            }
            var a = await GetChunkAtAsync(trans, b.Offset - 1).ConfigureAwait(false);

            if (a.Key == Slice.Nil)
            {
                return(false);                                // no previous chunk
            }
            if (a.Offset + a.Data.Count != b.Offset)
            {
                return(false);                                                 // chunks can't be joined
            }
            if (a.Data.Count + b.Data.Count > CHUNK_SMALL)
            {
                return(false);                                                        // chunks shouldn't be joined
            }
            // yay--merge chunks
            trans.Clear(b.Key);
            trans.Set(a.Key, a.Data + b.Data);
            return(true);
        }
        /// <summary>Add the value x to the counter.</summary>
        public async ValueTask Add(IFdbTransaction trans, long x)
        {
            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }

            var subspace = await this.Location.Resolve(trans);

            if (subspace == null)
            {
                throw new InvalidOperationException($"Location '{this.Location} referenced by High Contention Counter Layer was not found.");
            }

            trans.Set(subspace.Encode(RandomId()), this.Encoder.EncodeValue(x));

            // decide if we must coalesce
            //note: Random() is not thread-safe so we must lock
            bool coalesce;

            lock (this.Rng) { coalesce = this.Rng.NextDouble() < 0.1; }
            if (coalesce)
            {
                //REVIEW: 20 is too small if there is a lot of activity on the counter !
                BackgroundCoalesce(trans.Context.Database, 20, CancellationToken.None);
            }
        }
        private void SetSize([NotNull] IFdbTransaction trans, long size)
        {
            Contract.Requires(trans != null && size >= 0);

            //note: python code converts the size into a string
            trans.Set(SizeKey(), Slice.FromString(size.ToString()));
        }
Example #10
0
        /// <summary>Swap the items at positions i1 and i2.</summary>
        public async Task SwapAsync([NotNull] IFdbTransaction tr, long index1, long index2)
        {
            if (tr == null)
            {
                throw new ArgumentNullException("tr");
            }

            if (index1 < 0 || index2 < 0)
            {
                throw new IndexOutOfRangeException(String.Format("Indices ({0}, {1}) must be positive", index1, index2));
            }

            var k1 = GetKeyAt(index1);
            var k2 = GetKeyAt(index2);

            long currentSize = await ComputeSizeAsync(tr).ConfigureAwait(false);

            if (index1 >= currentSize || index2 >= currentSize)
            {
                throw new IndexOutOfRangeException(String.Format("Indices ({0}, {1}) are out of range", index1, index2));
            }

            var vs = await tr.GetValuesAsync(new[] { k1, k2 }).ConfigureAwait(false);

            var v1 = vs[0];
            var v2 = vs[1];

            if (!v2.IsNullOrEmpty)
            {
                tr.Set(k1, v2);
            }
            else if (v1.IsPresent && index1 < currentSize - 1)
            {
                tr.Clear(k1);
            }

            if (!v1.IsNullOrEmpty)
            {
                tr.Set(k2, v1);
            }
            else if (v2.IsPresent && index2 < currentSize - 1)
            {
                tr.Clear(k2);
            }
        }
Example #11
0
        /// <summary>Set the value at a particular index in the Vector.</summary>
        public void Set([NotNull] IFdbTransaction tr, long index, T value)
        {
            if (tr == null)
            {
                throw new ArgumentNullException("tr");
            }

            tr.Set(GetKeyAt(index), this.Encoder.EncodeValue(value));
        }
 /// <summary>Insert a newly created entity to the index</summary>
 /// <param name="trans">Transaction to use</param>
 /// <param name="id">Id of the new entity (that was never indexed before)</param>
 /// <param name="value">Value of this entity in the index</param>
 /// <returns>True if a value was inserted into the index; otherwise false (if value is null and <see cref="IndexNullValues"/> is false)</returns>
 public bool Add([NotNull] IFdbTransaction trans, TId id, TValue value)
 {
     if (this.IndexNullValues || value != null)
     {
         trans.Set(this.Location.Keys.Encode(value, id), Slice.Empty);
         return(true);
     }
     return(false);
 }
 /// <summary>Insert a newly created entity to the index</summary>
 /// <param name="trans">Transaction to use</param>
 /// <param name="id">Id of the new entity (that was never indexed before)</param>
 /// <param name="value">Value of this entity in the index</param>
 /// <returns>True if a value was inserted into the index; otherwise false (if value is null and <see cref="IndexNullValues"/> is false)</returns>
 public bool Add([NotNull] IFdbTransaction trans, TId id, TValue value)
 {
     if (this.IndexNullValues || value != null)
     {
         trans.Set(this.Subspace.Keys[value, id], Slice.Empty);
         return(true);
     }
     return(false);
 }
		private Task Scenario2(IFdbTransaction tr)
		{
			var location = FdbSubspace.Create(Slice.FromAscii("TEST"));
			tr.ClearRange(FdbKeyRange.StartsWith(location.Key));
			for (int i = 0; i < 10; i++)
			{
				tr.Set(location.Pack(i), Slice.FromString("value of " + i));
			}
			return Task.FromResult<object>(null);
		}
            public async Task InsertAsync(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.AtomicIncrement64(this.Subspace.Encode(level, prevKey));
                    }
                    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.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.Encode(level, prevKey), EncodeCount(newPrevCount));
                        trans.Set(this.Subspace.Encode(level, key), EncodeCount(count));
                    }
                }
            }
        private Task Scenario2(IFdbTransaction tr)
        {
            var location = FdbSubspace.Create(Slice.FromAscii("TEST"));

            tr.ClearRange(FdbKeyRange.StartsWith(location.Key));
            for (int i = 0; i < 10; i++)
            {
                tr.Set(location.Pack(i), Slice.FromString("value of " + i));
            }
            return(Task.FromResult <object>(null));
        }
Example #17
0
            /// <summary>Push a single item onto the end of the Vector.</summary>
            public async Task PushAsync(IFdbTransaction tr, T value)
            {
                if (tr == null)
                {
                    throw new ArgumentNullException(nameof(tr));
                }

                var size = await ComputeSizeAsync(tr).ConfigureAwait(false);

                tr.Set(GetKeyAt(size), this.Encoder.EncodeValue(value));
            }
        private async Task MakeSplitPointAsync([NotNull] IFdbTransaction trans, long offset)
        {
            Contract.Requires(trans != null && offset >= 0);

            var chunk = await GetChunkAtAsync(trans, offset).ConfigureAwait(false);

            if (chunk.Key == Slice.Nil)
            {
                return;                                     // already sparse
            }
            if (chunk.Offset == offset)
            {
                return;                                     // already a split point
            }
            int splitPoint;

            checked { splitPoint = (int)(offset - chunk.Offset); }
            trans.Set(DataKey(chunk.Offset), chunk.Data.Substring(0, splitPoint));
            trans.Set(DataKey(offset), chunk.Data.Substring(splitPoint));
        }
        private async Task PushAtAsync([NotNull] IFdbTransaction tr, T value, long index)
        {
            // Items are pushed on the queue at an (index, randomID) pair. Items pushed at the
            // same time will have the same index, and so their ordering will be random.
            // This makes pushes fast and usually conflict free (unless the queue becomes empty
            // during the push)

            Slice key = this.QueueItem.Keys.Encode(index, this.RandId());
            await tr.GetAsync(key).ConfigureAwait(false);

            tr.Set(key, this.Encoder.EncodeValue(value));
        }
        /// <summary>Add or update an entry in the map</summary>
        /// <param name="trans">Transaction used for the operation</param>
        /// <param name="id">Key of the entry to add or update</param>
        /// <param name="value">New value of the entry</param>
        /// <remarks>If the entry did not exist, it will be created. If not, its value will be replace with <paramref name="value"/>.</remarks>
        public void Set([NotNull] IFdbTransaction trans, TKey id, TValue value)
        {
            if (trans == null)
            {
                throw new ArgumentNullException("trans");
            }
            if (id == null)
            {
                throw new ArgumentNullException("id");
            }

            trans.Set(this.Location.Keys.Encode(id), this.ValueEncoder.EncodeValue(value));
        }
Example #21
0
        /// <summary>Update the indexed values of an entity</summary>
        /// <param name="trans">Transaction to use</param>
        /// <param name="id">Id of the entity that has changed</param>
        /// <param name="newValue">Previous value of this entity in the index</param>
        /// <param name="previousValue">New value of this entity in the index</param>
        /// <returns>True if a change was performed in the index; otherwise false (if <paramref name="previousValue"/> and <paramref name="newValue"/>)</returns>
        /// <remarks>If <paramref name="newValue"/> and <paramref name="previousValue"/> are identical, then nothing will be done. Otherwise, the old index value will be deleted and the new value will be added</remarks>
        public async Task <bool> UpdateAsync([NotNull] IFdbTransaction trans, long id, TValue newValue, TValue previousValue)
        {
            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }

            if (!this.ValueComparer.Equals(newValue, previousValue))
            {
                // remove previous value
                if (this.IndexNullValues || previousValue != null)
                {
                    var key  = this.Location.Keys.Encode(previousValue);
                    var data = await trans.GetAsync(key).ConfigureAwait(false);

                    if (data.HasValue)
                    {
                        var builder = new CompressedBitmapBuilder(data);
                        builder.Clear((int)id);                         //BUGBUG: 64 bit id!
                        trans.Set(key, builder.ToSlice());
                    }
                }

                // add new value
                if (this.IndexNullValues || newValue != null)
                {
                    var key  = this.Location.Keys.Encode(newValue);
                    var data = await trans.GetAsync(key).ConfigureAwait(false);

                    var builder = data.HasValue ? new CompressedBitmapBuilder(data) : CompressedBitmapBuilder.Empty;
                    builder.Set((int)id);                     //BUGBUG: 64 bit id!
                    trans.Set(key, builder.ToSlice());
                }

                // cannot be both null, so we did at least something)
                return(true);
            }
            return(false);
        }
        /// <summary>Add or update an entry in the map</summary>
        /// <param name="trans">Transaction used for the operation</param>
        /// <param name="id">Key of the entry to add or update</param>
        /// <param name="value">New value of the entry</param>
        /// <remarks>If the entry did not exist, it will be created. If not, its value will be replace with <paramref name="value"/>.</remarks>
        public void Set([NotNull] IFdbTransaction trans, TKey id, TValue value)
        {
            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            trans.Set(this.Subspace.Keys[id], this.ValueEncoder.EncodeValue(value));
        }
		private async Task Scenario5(IFdbTransaction tr)
		{
			var location = FdbSubspace.Create(Slice.FromAscii("TEST"));

			//tr.Set(location.Pack(42), Slice.FromString("42"));
			//tr.Set(location.Pack(50), Slice.FromString("50"));
			//tr.Set(location.Pack(60), Slice.FromString("60"));

			var x = await tr.GetKeyAsync(FdbKeySelector.LastLessThan(location.Pack(49)));
			Console.WriteLine(x);

			tr.Set(location.Pack("FOO"), Slice.FromString("BAR"));

		}
Example #24
0
        /// <summary>
        /// Drop a student from a class
        /// </summary>
        public async Task Drop(IFdbTransaction tr, string s, string c)
        {
            var rec = AttendsKey(s, c);

            if ((await tr.GetAsync(rec)).IsNullOrEmpty)
            {             // not taking this class
                return;
            }

            var students = Int32.Parse((await tr.GetAsync(ClassKey(c))).ToStringAscii());

            tr.Set(ClassKey(c), Slice.FromStringAscii((students + 1).ToString()));
            tr.Clear(rec);
        }
        private async Task Scenario5(IFdbTransaction tr)
        {
            var location = FdbSubspace.Create(Slice.FromAscii("TEST"));

            //tr.Set(location.Pack(42), Slice.FromString("42"));
            //tr.Set(location.Pack(50), Slice.FromString("50"));
            //tr.Set(location.Pack(60), Slice.FromString("60"));

            var x = await tr.GetKeyAsync(FdbKeySelector.LastLessThan(location.Pack(49)));

            Console.WriteLine(x);

            tr.Set(location.Pack("FOO"), Slice.FromString("BAR"));
        }
Example #26
0
            private async Task <Slice> InternSlowAsync(IFdbTransaction trans, string value)
            {
                var stringKey = StringKey(value);

                var uid = await trans.GetAsync(stringKey).ConfigureAwait(false);

                if (uid.IsNull)
                {
#if DEBUG_STRING_INTERNING
                    Debug.WriteLine("_ not found in db, will create...");
#endif

                    uid = await FindUidAsync(trans).ConfigureAwait(false);

                    if (uid.IsNull)
                    {
                        throw new InvalidOperationException("Failed to allocate a new uid while attempting to intern a string");
                    }
#if DEBUG_STRING_INTERNING
                    Debug.WriteLine("> using new uid " + uid.ToBase64());
#endif

                    trans.Set(UidKey(uid), Slice.FromString(value));
                    trans.Set(stringKey, uid);

                    //BUGBUG: if the transaction fails to commit, we will inserted a bad value in the cache!
                    this.Layer.AddToCache(value, uid);
                }
                else
                {
#if DEBUG_STRING_INTERNING
                    Debug.WriteLine("> found in db with uid " + uid.ToBase64());
#endif
                }

                return(uid);
            }
        private async Task <Slice> AddConflictedPopAsync([NotNull] IFdbTransaction tr, bool forced)
        {
            long index = await GetNextIndexAsync(tr.Snapshot, this.ConflictedPop).ConfigureAwait(false);

            if (index == 0 && !forced)
            {
                return(Slice.Nil);
            }

            Slice waitKey = this.ConflictedPop.Keys.Encode(index, this.RandId());
            await tr.GetAsync(waitKey).ConfigureAwait(false);

            tr.Set(waitKey, Slice.Empty);
            return(waitKey);
        }
Example #28
0
        /// <summary>Grow or shrink the size of the Vector.</summary>
        public async Task ResizeAsync([NotNull] IFdbTransaction tr, long length)
        {
            if (tr == null)
            {
                throw new ArgumentNullException("tr");
            }

            long currentSize = await ComputeSizeAsync(tr).ConfigureAwait(false);

            if (length < currentSize)
            {
                tr.ClearRange(GetKeyAt(length), this.Subspace.Keys.ToRange().End);

                // Check if the new end of the vector was being sparsely represented
                if (await ComputeSizeAsync(tr).ConfigureAwait(false) < length)
                {
                    tr.Set(GetKeyAt(length - 1), this.Encoder.EncodeValue(this.DefaultValue));
                }
            }
            else if (length > currentSize)
            {
                tr.Set(GetKeyAt(length - 1), this.Encoder.EncodeValue(this.DefaultValue));
            }
        }
        private async Task PushQueueAsync(IFdbTransaction tr, IDynamicKeySubspace queue, Slice taskId)
        {
            //TODO: use a high contention algo ?
            // - must support Push and Pop
            // - an empty queue must correspond to an empty subspace

            // get the current size of the queue
            var range   = queue.Keys.ToRange();
            var lastKey = await tr.Snapshot.GetKeyAsync(KeySelector.LastLessThan(range.End)).ConfigureAwait(false);

            int count = lastKey < range.Begin ? 0 : queue.Keys.DecodeFirst <int>(lastKey) + 1;

            // set the value
            tr.Set(queue.Keys.Encode(count, GetRandomId()), taskId);
        }
            internal async Task SetupLevelsAsync(IFdbTransaction trans)
            {
                var ks = Enumerable.Range(0, MAX_LEVELS)
                         .Select((l) => this.Subspace.Encode(l, Slice.Empty))
                         .ToList();

                var res = await trans.GetValuesAsync(ks).ConfigureAwait(false);

                for (int l = 0; l < res.Length; l++)
                {
                    if (res[l].IsNull)
                    {
                        trans.Set(ks[l], EncodeCount(0));
                    }
                }
            }
Example #31
0
        private async Task <List <PerformanceRecord> > WriteInternalAsync(string operation,
                                                                          IEnumerator <TestData> enumerator,
                                                                          long itemsPerTransaction, long numberOfTransactions, PerfTracker perfTracker, FdbDatabase db)
        {
            var sw = new Stopwatch();

            byte[] valueToWrite = null;
            var    records      = new List <PerformanceRecord>();

            var location = db.GlobalSpace;

            sw.Restart();
            for (int transactions = 0; transactions < numberOfTransactions; transactions++)
            {
                sw.Restart();
                using (IFdbTransaction tx = db.BeginTransaction())
                {
                    for (int i = 0; i < itemsPerTransaction; i++)
                    {
                        enumerator.MoveNext();

                        valueToWrite = GetValueToWrite(valueToWrite, enumerator.Current.ValueSize);

                        tx.Set(location.Pack(enumerator.Current.Id), Slice.Create(valueToWrite));
                    }

                    await tx.CommitAsync();

                    perfTracker.Record(sw.ElapsedMilliseconds);
                }

                sw.Stop();

                records.Add(new PerformanceRecord
                {
                    Operation      = operation,
                    Time           = DateTime.Now,
                    Duration       = sw.ElapsedMilliseconds,
                    ProcessedItems = itemsPerTransaction
                });
            }

            sw.Stop();

            return(records);
        }
Example #32
0
        public void SetValue(IFdbTransaction trans, IVarTuple id, string field, Slice value)
        {
            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }
            if (string.IsNullOrEmpty(field))
            {
                throw new ArgumentNullException(nameof(field));
            }

            trans.Set(GetFieldKey(id, field), value);
        }
        private void WriteToSparse([NotNull] IFdbTransaction trans, long offset, Slice data)
        {
            Contract.Requires(trans != null && offset >= 0);

            if (data.IsNullOrEmpty)
            {
                return;
            }

            int chunks    = (int)((data.Count + CHUNK_LARGE - 1) / CHUNK_LARGE);
            int chunkSize = (data.Count + chunks) / chunks;

            for (int n = 0; n < data.Count; n += chunkSize)
            {
                trans.Set(DataKey(offset + n), data[n, n + chunkSize]);
            }
        }
		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);
		}
		private async Task PushQueueAsync(IFdbTransaction tr, FdbSubspace queue, Slice taskId)
		{
			//TODO: use a high contention algo ?
			// - must support Push and Pop
			// - an empty queue must correspond to an empty subspace

			// get the current size of the queue
			var range = queue.ToRange();
			var lastKey = await tr.Snapshot.GetKeyAsync(FdbKeySelector.LastLessThan(range.End)).ConfigureAwait(false);
			int count = lastKey < range.Begin ? 0 : queue.Unpack(lastKey).Get<int>(0) + 1;

			// set the value
			tr.Set(queue.Pack(count, GetRandomId()), taskId);
		}
		private void WriteToSparse(IFdbTransaction trans, long offset, Slice data)
		{
			Contract.Requires(trans != null && offset >= 0);

			if (data.IsNullOrEmpty) return;

			int chunks = (int)((data.Count + CHUNK_LARGE - 1) / CHUNK_LARGE);
			int chunkSize = (data.Count + chunks) / chunks;

			for (int n = 0; n < data.Count; n += chunkSize)
			{
				trans.Set(DataKey(offset + n), data[n, n + chunkSize]);
			}
		}
		private void SetSize(IFdbTransaction trans, long size)
		{
			Contract.Requires(trans != null && size >= 0);

			//note: python code converts the size into a string
			trans.Set(SizeKey(), Slice.FromString(size.ToString()));
		}
Example #38
0
		/// <summary>Add the value x to the counter.</summary>
		public void Add(IFdbTransaction trans, long x)
		{
			if (trans == null) throw new ArgumentNullException("trans");

			trans.Set(this.Subspace.Pack(RandomId()), this.Encoder.EncodeValue(x));

			// decide if we must coalesce
			//note: Random() is not thread-safe so we must lock
			bool coalesce;
			lock (this.Rng) { coalesce = this.Rng.NextDouble() < 0.1; }
			if (coalesce)
			{
				//REVIEW: 20 is too small if there is a lot of activity on the counter !
				BackgroundCoalesce(20, CancellationToken.None);
			}
		}
		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 static void SetLayer(IFdbTransaction trans, FdbSubspace subspace, Slice layer)
		{
			if (layer.IsNull) layer = Slice.Empty;
			trans.Set(subspace.Pack(LayerSuffix), layer);
		}
		private async Task<bool> TryRemoteSplitPointAsync(IFdbTransaction trans, long offset)
		{
			Contract.Requires(trans != null && offset >= 0);

			var b = await GetChunkAtAsync(trans, offset).ConfigureAwait(false);
			if (b.Offset == 0 || b.Key == Slice.Nil) return false; // in sparse region, or at beginning

			var a = await GetChunkAtAsync(trans, b.Offset - 1).ConfigureAwait(false);
			if (a.Key == Slice.Nil) return false; // no previous chunk

			if (a.Offset + a.Data.Count != b.Offset) return false; // chunks can't be joined
			if (a.Data.Count + b.Data.Count > CHUNK_SMALL) return false;  // chunks shouldn't be joined
			// yay--merge chunks
			trans.Clear(b.Key);
			trans.Set(a.Key, a.Data + b.Data);
			return true;
		}
		public void Set(IFdbTransaction trans, IFdbTuple id, IEnumerable<KeyValuePair<string, Slice>> fields)
		{
			if (trans == null) throw new ArgumentNullException("trans");
			if (id == null) throw new ArgumentNullException("id");
			if (fields == null) throw new ArgumentNullException("fields");

			foreach (var field in fields)
			{
				if (string.IsNullOrEmpty(field.Key)) throw new ArgumentException("Field cannot have an empty name", "fields");
				trans.Set(GetFieldKey(id, field.Key), field.Value);
			}
		}
		/// <summary>
		/// Drop a student from a class
		/// </summary>
		public async Task Drop(IFdbTransaction tr, string s, string c)
		{
			var rec = AttendsKey(s, c);
			if ((await tr.GetAsync(rec)).IsNullOrEmpty)
			{ // not taking this class
				return;
			}

			var students = Int32.Parse((await tr.GetAsync(ClassKey(c))).ToAscii());
			tr.Set(ClassKey(c), Slice.FromAscii((students + 1).ToString()));
			tr.Clear(rec);
		}
		/// <summary>
		/// Signup a student to a class
		/// </summary>
		public async Task Signup(IFdbTransaction tr, string s, string c)
		{
			var rec = AttendsKey(s, c);

			if ((await tr.GetAsync(rec)).IsPresent)
			{ // already signed up
				return;
			}
			int seatsLeft = Int32.Parse((await tr.GetAsync(ClassKey(c))).ToAscii());
			if (seatsLeft <= 0)
			{
				throw new InvalidOperationException("No remaining seats");
			}

			var classes = await tr.GetRange(AttendsKeys(s)).ToListAsync();
			if (classes.Count >= 5) throw new InvalidOperationException("Too many classes");

			tr.Set(ClassKey(c), Slice.FromAscii((seatsLeft - 1).ToString()));
			tr.Set(rec, Slice.Empty);
		}
		private async Task SetupLevelsAsync(IFdbTransaction trans)
		{
			var ks = Enumerable.Range(0, MAX_LEVELS)
				.Select((l) => this.Subspace.Pack(l, Slice.Empty))
				.ToList();

			var res = await trans.GetValuesAsync(ks).ConfigureAwait(false);
			for (int l = 0; l < res.Length; l++)
			{
				//Console.WriteLine(ks[l]);
				if (res[l].IsNull) trans.Set(ks[l], EncodeCount(0));
			}
		}
		internal async Task<FdbDirectorySubspace> MoveInternalAsync(IFdbTransaction trans, IFdbTuple oldPath, IFdbTuple newPath, bool throwOnError)
		{
			Contract.Requires(trans != null && oldPath != null && newPath != null);

			if (oldPath.Count == 0)
			{
				throw new InvalidOperationException("The root directory may not be moved.");
			}
			if (newPath.Count == 0)
			{
				throw new InvalidOperationException("The root directory cannot be overwritten.");
			}
			if (newPath.StartsWith(oldPath))
			{
				throw new InvalidOperationException(string.Format("The destination directory({0}) cannot be a subdirectory of the source directory({1}).", newPath, oldPath));
			}

			await CheckWriteVersionAsync(trans).ConfigureAwait(false);

			var oldNode = await FindAsync(trans, oldPath).ConfigureAwait(false);
			if (!oldNode.Exists)
			{
				if (throwOnError) throw new InvalidOperationException(string.Format("The source directory '{0}' does not exist.", oldPath));
				return null;
			}

			var newNode = await FindAsync(trans, newPath).ConfigureAwait(false);

			// we have already checked that old and new are under this partition path, but one of them (or both?) could be under a sub-partition..
			if (oldNode.IsInPartition(false) || newNode.IsInPartition(false))
			{
				if (!oldNode.IsInPartition(false) || !newNode.IsInPartition(false) || !FdbTuple.Equals(oldNode.Path, newNode.Path))
				{
					throw new InvalidOperationException("Cannot move between partitions.");
				}
				// both nodes are in the same sub-partition, delegate to it
				return await GetPartitionForNode(newNode).DirectoryLayer.MoveInternalAsync(trans, oldNode.PartitionSubPath, newNode.PartitionSubPath, throwOnError).ConfigureAwait(false);
			}

			if (newNode.Exists)
			{
				if (throwOnError) throw new InvalidOperationException(string.Format("The destination directory '{0}' already exists. Remove it first.", newPath));
				return null;
			}

			var parentNode = await FindAsync(trans, newPath.Substring(0, newPath.Count - 1)).ConfigureAwait(false);
			if (!parentNode.Exists)
			{
				if (throwOnError) throw new InvalidOperationException(string.Format("The parent of the destination directory '{0}' does not exist. Create it first.", newPath));
				return null;
			}

			trans.Set(GetSubDirKey(parentNode.Subspace, newPath.Get<string>(-1)), this.NodeSubspace.UnpackSingle<Slice>(oldNode.Subspace.Key));
			await RemoveFromParent(trans, oldPath).ConfigureAwait(false);

			return ContentsOfNode(oldNode.Subspace, newPath, oldNode.Layer);
		}
		// Compare the behavior of the MemoryDB against a FoundationDB database

		private async Task Scenario1(IFdbTransaction tr)
		{
			tr.Set(Slice.FromAscii("hello"), Slice.FromAscii("world!"));
			tr.Clear(Slice.FromAscii("removed"));
			var result = await tr.GetAsync(Slice.FromAscii("narf"));
		}
		public void SetValue(IFdbTransaction trans, IFdbTuple id, string field, Slice value)
		{
			if (trans == null) throw new ArgumentNullException("trans");
			if (id == null) throw new ArgumentNullException("id");
			if (string.IsNullOrEmpty(field)) throw new ArgumentNullException("field");

			trans.Set(GetFieldKey(id, field), value);
		}
		private void InitializeDirectory(IFdbTransaction trans)
		{
			// Set the version key
			var writer = new SliceWriter(3 * 4);
			writer.WriteFixed32((uint)LayerVersion.Major);
			writer.WriteFixed32((uint)LayerVersion.Minor);
			writer.WriteFixed32((uint)LayerVersion.Build);
			trans.Set(this.RootNode.Pack(VersionKey), writer.ToSlice());
		}
		/// <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...
			}
		}
		private async Task MakeSplitPointAsync(IFdbTransaction trans, long offset)
		{
			Contract.Requires(trans != null && offset >= 0);

			var chunk = await GetChunkAtAsync(trans, offset).ConfigureAwait(false);
			if (chunk.Key == Slice.Nil) return; // already sparse
			if (chunk.Offset == offset) return; // already a split point

			int splitPoint;
			checked { splitPoint = (int)(offset - chunk.Offset); }
			trans.Set(DataKey(chunk.Offset), chunk.Data.Substring(0, splitPoint));
			trans.Set(DataKey(offset), chunk.Data.Substring(splitPoint));
		}