PageRenderTime 76ms CodeModel.GetById 13ms app.highlight 51ms RepoModel.GetById 1ms app.codeStats 0ms

/Raven.Database/Storage/Voron/StorageActions/MappedResultsStorageActions.cs

https://github.com/nwendel/ravendb
C# | 1076 lines | 888 code | 188 blank | 0 comment | 95 complexity | f08086055baf43cdd977b4080f0e5734 MD5 | raw file
   1using System.Diagnostics;
   2using System.Text;
   3
   4using Raven.Abstractions.Util.Encryptors;
   5using Raven.Abstractions.Util.Streams;
   6using Raven.Database.Util;
   7
   8namespace Raven.Database.Storage.Voron.StorageActions
   9{
  10	using System;
  11	using System.Collections.Generic;
  12	using System.Globalization;
  13	using System.IO;
  14	using System.Linq;
  15
  16	using Raven.Abstractions;
  17	using Raven.Abstractions.Data;
  18	using Raven.Abstractions.Extensions;
  19	using Raven.Abstractions.MEF;
  20	using Raven.Database.Impl;
  21	using Raven.Database.Indexing;
  22	using Raven.Database.Plugins;
  23	using Raven.Database.Storage.Voron.Impl;
  24	using Raven.Json.Linq;
  25
  26	using global::Voron;
  27	using global::Voron.Impl;
  28
  29	using Index = Raven.Database.Storage.Voron.Impl.Index;
  30
  31	public class MappedResultsStorageActions : StorageActionsBase, IMappedResultsStorageAction
  32	{
  33		private readonly TableStorage tableStorage;
  34
  35		private readonly IUuidGenerator generator;
  36
  37		private readonly Reference<WriteBatch> writeBatch;
  38
  39		private readonly OrderedPartCollection<AbstractDocumentCodec> documentCodecs;
  40
  41        public MappedResultsStorageActions(TableStorage tableStorage, IUuidGenerator generator, OrderedPartCollection<AbstractDocumentCodec> documentCodecs, Reference<SnapshotReader> snapshot, Reference<WriteBatch> writeBatch, IBufferPool bufferPool)
  42			: base(snapshot, bufferPool)
  43		{
  44			this.tableStorage = tableStorage;
  45			this.generator = generator;
  46			this.documentCodecs = documentCodecs;
  47			this.writeBatch = writeBatch;
  48		}
  49
  50		public IEnumerable<ReduceKeyAndCount> GetKeysStats(int view, int start, int pageSize)
  51		{
  52			var reduceKeyCountsByView = tableStorage.ReduceKeyCounts.GetIndex(Tables.ReduceKeyCounts.Indices.ByView);
  53			using (var iterator = reduceKeyCountsByView.MultiRead(Snapshot, CreateKey(view)))
  54			{
  55				if (!iterator.Seek(Slice.BeforeAllKeys) || !iterator.Skip(start))
  56					yield break;
  57
  58				var count = 0;
  59				do
  60				{
  61					ushort version;
  62					var value = LoadJson(tableStorage.ReduceKeyCounts, iterator.CurrentKey, writeBatch.Value, out version);
  63
  64					Debug.Assert(value != null);
  65					yield return new ReduceKeyAndCount
  66								 {
  67									 Count = value.Value<int>("mappedItemsCount"),
  68									 Key = value.Value<string>("reduceKey")
  69								 };
  70
  71					count++;
  72				}
  73				while (iterator.MoveNext() && count < pageSize);
  74			}
  75		}
  76
  77		public void PutMappedResult(int view, string docId, string reduceKey, RavenJObject data)
  78		{
  79			var mappedResultsByViewAndDocumentId = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByViewAndDocumentId);
  80			var mappedResultsByView = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByView);
  81			var mappedResultsByViewAndReduceKey = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByViewAndReduceKey);
  82			var mappedResultsByViewAndReduceKeyAndSourceBucket = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByViewAndReduceKeyAndSourceBucket);
  83
  84			var mappedResultsData = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.Data);
  85
  86            var ms = CreateStream();
  87			using (var stream = documentCodecs.Aggregate((Stream) new UndisposableStream(ms), (ds, codec) => codec.Value.Encode(reduceKey, data, null, ds)))
  88			{
  89				data.WriteTo(stream);
  90				stream.Flush();
  91			}
  92
  93			var id = generator.CreateSequentialUuid(UuidType.MappedResults);
  94			var idAsString = id.ToString();
  95			var bucket = IndexingUtil.MapBucket(docId);
  96
  97		    var reduceKeyHash = HashKey(reduceKey);
  98
  99			tableStorage.MappedResults.Add(
 100				writeBatch.Value,
 101				idAsString,
 102				new RavenJObject
 103				{
 104					{ "view", view },
 105					{ "reduceKey", reduceKey },
 106					{ "docId", docId },
 107					{ "etag", id.ToByteArray() },
 108					{ "bucket", bucket },
 109					{ "timestamp", SystemTime.UtcNow }
 110				}, 0);
 111
 112			ms.Position = 0;
 113			mappedResultsData.Add(writeBatch.Value, idAsString, ms, 0);
 114
 115			mappedResultsByViewAndDocumentId.MultiAdd(writeBatch.Value, CreateKey(view, docId), idAsString);
 116			mappedResultsByView.MultiAdd(writeBatch.Value, CreateKey(view), idAsString);
 117            mappedResultsByViewAndReduceKey.MultiAdd(writeBatch.Value, CreateKey(view, reduceKey, reduceKeyHash), idAsString);
 118            mappedResultsByViewAndReduceKeyAndSourceBucket.MultiAdd(writeBatch.Value, CreateKey(view, reduceKey, reduceKeyHash, bucket), idAsString);
 119		}
 120
 121		public void IncrementReduceKeyCounter(int view, string reduceKey, int val)
 122		{
 123		    var reduceKeyHash = HashKey(reduceKey);
 124            var key = CreateKey(view, reduceKey, reduceKeyHash);
 125
 126			ushort version;
 127			var value = LoadJson(tableStorage.ReduceKeyCounts, key, writeBatch.Value, out version);
 128
 129			var newValue = val;
 130			if (value != null)
 131				newValue += value.Value<int>("mappedItemsCount");
 132
 133			AddReduceKeyCount(key, view, reduceKey, newValue, version);
 134		}
 135
 136		private void DecrementReduceKeyCounter(int view, string reduceKey, int val)
 137		{
 138            var reduceKeyHash = HashKey(reduceKey);
 139			var key = CreateKey(view, reduceKey, reduceKeyHash);
 140
 141			ushort reduceKeyCountVersion;
 142			var reduceKeyCount = LoadJson(tableStorage.ReduceKeyCounts, key, writeBatch.Value, out reduceKeyCountVersion);
 143
 144			var newValue = -val;
 145			if (reduceKeyCount != null)
 146			{
 147				var currentValue = reduceKeyCount.Value<int>("mappedItemsCount");
 148				if (currentValue == val)
 149				{
 150					var reduceKeyTypeVersion = tableStorage.ReduceKeyTypes.ReadVersion(Snapshot, key);
 151
 152					DeleteReduceKeyCount(key, view, reduceKeyCountVersion);
 153					DeleteReduceKeyType(key, view, reduceKeyTypeVersion);
 154					return;
 155				}
 156
 157				newValue += currentValue;
 158			}
 159
 160			AddReduceKeyCount(key, view, reduceKey, newValue, reduceKeyCountVersion);
 161		}
 162
 163		public void DeleteMappedResultsForDocumentId(string documentId, int view, Dictionary<ReduceKeyAndBucket, int> removed)
 164		{
 165			var viewAndDocumentId = CreateKey(view, documentId);
 166
 167			var mappedResultsByViewAndDocumentId = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByViewAndDocumentId);
 168			using (var iterator = mappedResultsByViewAndDocumentId.MultiRead(Snapshot, viewAndDocumentId))
 169			{
 170				if (!iterator.Seek(Slice.BeforeAllKeys))
 171					return;
 172
 173				do
 174				{
 175					var id = iterator.CurrentKey.Clone();
 176
 177					ushort version;
 178					var value = LoadJson(tableStorage.MappedResults, id, writeBatch.Value, out version);
 179					var reduceKey = value.Value<string>("reduceKey");
 180					var bucket = value.Value<int>("bucket");
 181
 182					DeleteMappedResult(id, view, documentId, reduceKey, bucket.ToString(CultureInfo.InvariantCulture));
 183
 184					var reduceKeyAndBucket = new ReduceKeyAndBucket(bucket, reduceKey);
 185					removed[reduceKeyAndBucket] = removed.GetOrDefault(reduceKeyAndBucket) + 1;
 186				}
 187				while (iterator.MoveNext());
 188			}
 189		}
 190
 191		public void UpdateRemovedMapReduceStats(int view, Dictionary<ReduceKeyAndBucket, int> removed)
 192		{
 193			foreach (var keyAndBucket in removed)
 194			{
 195				DecrementReduceKeyCounter(view, keyAndBucket.Key.ReduceKey, keyAndBucket.Value);
 196			}
 197		}
 198
 199		public void DeleteMappedResultsForView(int view)
 200		{
 201			var deletedReduceKeys = new List<string>();
 202			var mappedResultsByView = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByView);
 203
 204			using (var iterator = mappedResultsByView.MultiRead(Snapshot, CreateKey(view)))
 205			{
 206				if (!iterator.Seek(Slice.BeforeAllKeys))
 207					return;
 208
 209				do
 210				{
 211					var id = iterator.CurrentKey.Clone();
 212
 213					ushort version;
 214					var value = LoadJson(tableStorage.MappedResults, id, writeBatch.Value, out version);
 215					var reduceKey = value.Value<string>("reduceKey");
 216					var bucket = value.Value<string>("bucket");
 217
 218					DeleteMappedResult(id, view, value.Value<string>("docId"), reduceKey, bucket);
 219
 220					deletedReduceKeys.Add(reduceKey);
 221				}
 222				while (iterator.MoveNext());
 223			}
 224
 225			foreach (var g in deletedReduceKeys.GroupBy(x => x, StringComparer.InvariantCultureIgnoreCase))
 226			{
 227				DecrementReduceKeyCounter(view, g.Key, g.Count());
 228			}
 229		}
 230
 231		public IEnumerable<string> GetKeysForIndexForDebug(int view, string startsWith, string sourceId, int start, int take)
 232		{
 233			var mappedResultsByView = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByView);
 234			using (var iterator = mappedResultsByView.MultiRead(Snapshot, CreateKey(view)))
 235			{
 236				if (!iterator.Seek(Slice.BeforeAllKeys)) 
 237					return Enumerable.Empty<string>();
 238
 239				var results = new List<string>();
 240				do
 241				{
 242					ushort version;
 243					var value = LoadJson(tableStorage.MappedResults, iterator.CurrentKey, writeBatch.Value, out version);
 244
 245					if (string.IsNullOrEmpty(sourceId) == false)
 246					{
 247						var docId = value.Value<string>("docId");
 248						if (string.Equals(sourceId, docId, StringComparison.OrdinalIgnoreCase) == false)
 249							continue;
 250					}
 251
 252					var reduceKey = value.Value<string>("reduceKey");
 253
 254					if (string.IsNullOrEmpty(startsWith) == false && reduceKey.StartsWith(startsWith, StringComparison.OrdinalIgnoreCase) == false)
 255						continue;
 256
 257					results.Add(reduceKey);
 258				}
 259				while (iterator.MoveNext());
 260
 261				return results
 262					.Distinct()
 263					.Skip(start)
 264					.Take(take);
 265			}
 266		}
 267
 268        public IEnumerable<MappedResultInfo> GetMappedResultsForDebug(int view, string reduceKey, int start, int take)
 269        {
 270            var reduceKeyHash = HashKey(reduceKey);
 271            var viewAndReduceKey = CreateKey(view, reduceKey, reduceKeyHash);
 272            var mappedResultsByViewAndReduceKey = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByViewAndReduceKey);
 273            var mappedResultsData = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.Data);
 274
 275            using (var iterator = mappedResultsByViewAndReduceKey.MultiRead(Snapshot, viewAndReduceKey))
 276            {
 277                if (!iterator.Seek(Slice.BeforeAllKeys) || !iterator.Skip(start))
 278                    yield break;
 279
 280                var count = 0;
 281                do
 282                {
 283                    ushort version;
 284                    var value = LoadJson(tableStorage.MappedResults, iterator.CurrentKey, writeBatch.Value, out version);
 285                    var size = tableStorage.MappedResults.GetDataSize(Snapshot, iterator.CurrentKey);
 286                    yield return new MappedResultInfo
 287                    {
 288                        ReduceKey = value.Value<string>("reduceKey"),
 289                        Etag = Etag.Parse(value.Value<byte[]>("etag")),
 290                        Timestamp = value.Value<DateTime>("timestamp"),
 291                        Bucket = value.Value<int>("bucket"),
 292                        Source = value.Value<string>("docId"),
 293                        Size = size,
 294                        Data = LoadMappedResult(iterator.CurrentKey, value, mappedResultsData)
 295                    };
 296
 297                    count++;
 298                }
 299                while (iterator.MoveNext() && count < take);
 300            }
 301        }
 302
 303        public IEnumerable<string> GetSourcesForIndexForDebug(int view, string startsWith, int take)
 304        {
 305            var mappedResultsByView = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByView);
 306            using (var iterator = mappedResultsByView.MultiRead(Snapshot, CreateKey(view)))
 307            {
 308                if (!iterator.Seek(Slice.BeforeAllKeys))
 309                    return Enumerable.Empty<string>();
 310
 311                var results = new HashSet<string>();
 312                do
 313                {
 314                    ushort version;
 315                    var value = LoadJson(tableStorage.MappedResults, iterator.CurrentKey, writeBatch.Value, out version);
 316
 317                    var docId = value.Value<string>("docId");
 318
 319                    if (string.IsNullOrEmpty(startsWith) == false && docId.StartsWith(startsWith, StringComparison.OrdinalIgnoreCase) == false)
 320                        continue;
 321
 322                    results.Add(docId);
 323                }
 324                while (iterator.MoveNext() && results.Count <= take);
 325
 326                return results;
 327            }
 328        }
 329       
 330
 331		public IEnumerable<MappedResultInfo> GetReducedResultsForDebug(int view, string reduceKey, int level, int start, int take)
 332		{
 333		    var reduceKeyHash = HashKey(reduceKey);
 334            var viewAndReduceKeyAndLevel = CreateKey(view, reduceKey, reduceKeyHash, level);
 335			var reduceResultsByViewAndReduceKeyAndLevel =
 336				tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.ByViewAndReduceKeyAndLevel);
 337			var reduceResultsData = tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.Data);
 338
 339			using (var iterator = reduceResultsByViewAndReduceKeyAndLevel.MultiRead(Snapshot, viewAndReduceKeyAndLevel))
 340			{
 341				if (!iterator.Seek(Slice.BeforeAllKeys) || !iterator.Skip(start))
 342					yield break;
 343
 344				var count = 0;
 345				do
 346				{
 347					ushort version;
 348					var value = LoadJson(tableStorage.ReduceResults, iterator.CurrentKey, writeBatch.Value, out version);
 349					var size = tableStorage.ReduceResults.GetDataSize(Snapshot, iterator.CurrentKey);
 350
 351					yield return
 352						new MappedResultInfo
 353						{
 354							ReduceKey = value.Value<string>("reduceKey"),
 355							Etag = Etag.Parse(value.Value<byte[]>("etag")),
 356							Timestamp = value.Value<DateTime>("timestamp"),
 357							Bucket = value.Value<int>("bucket"),
 358							Source = value.Value<string>("sourceBucket"),
 359							Size = size,
 360							Data = LoadMappedResult(iterator.CurrentKey, value, reduceResultsData)
 361						};
 362
 363					count++;
 364				}
 365				while (iterator.MoveNext() && count < take);
 366			}
 367		}
 368
 369		public IEnumerable<ScheduledReductionDebugInfo> GetScheduledReductionForDebug(int view, int start, int take)
 370		{
 371			var scheduledReductionsByView = tableStorage.ScheduledReductions.GetIndex(Tables.ScheduledReductions.Indices.ByView);
 372			using (var iterator = scheduledReductionsByView.MultiRead(Snapshot, CreateKey(view)))
 373			{
 374				if (!iterator.Seek(Slice.BeforeAllKeys) || !iterator.Skip(start))
 375					yield break;
 376
 377				var count = 0;
 378				do
 379				{
 380					ushort version;
 381					var value = LoadJson(tableStorage.ScheduledReductions, iterator.CurrentKey, writeBatch.Value, out version);
 382
 383					yield return new ScheduledReductionDebugInfo
 384					{
 385						Key = value.Value<string>("reduceKey"),
 386						Bucket = value.Value<int>("bucket"),
 387						Etag = new Guid(value.Value<byte[]>("etag")),
 388						Level = value.Value<int>("level"),
 389						Timestamp = value.Value<DateTime>("timestamp"),
 390					};
 391
 392					count++;
 393				}
 394				while (iterator.MoveNext() && count < take);
 395			}
 396		}
 397
 398		public void ScheduleReductions(int view, int level, ReduceKeyAndBucket reduceKeysAndBuckets)
 399		{
 400			var scheduledReductionsByView = tableStorage.ScheduledReductions.GetIndex(Tables.ScheduledReductions.Indices.ByView);
 401			var scheduledReductionsByViewAndLevelAndReduceKey = tableStorage.ScheduledReductions.GetIndex(Tables.ScheduledReductions.Indices.ByViewAndLevelAndReduceKey);
 402
 403			var id = generator.CreateSequentialUuid(UuidType.ScheduledReductions);
 404			var idAsString = id.ToString();
 405		    var reduceHashKey = HashKey(reduceKeysAndBuckets.ReduceKey);
 406
 407			tableStorage.ScheduledReductions.Add(writeBatch.Value, idAsString, new RavenJObject
 408			{
 409				{"view", view},
 410				{"reduceKey", reduceKeysAndBuckets.ReduceKey},
 411				{"bucket", reduceKeysAndBuckets.Bucket},
 412				{"level", level},
 413				{"etag", id.ToByteArray()},
 414				{"timestamp", SystemTime.UtcNow}
 415			});
 416
 417			scheduledReductionsByView.MultiAdd(writeBatch.Value, CreateKey(view), idAsString);
 418			scheduledReductionsByViewAndLevelAndReduceKey.MultiAdd(writeBatch.Value, CreateKey(view, level, reduceKeysAndBuckets.ReduceKey, reduceHashKey), idAsString);
 419		}
 420
 421		public IEnumerable<MappedResultInfo> GetItemsToReduce(GetItemsToReduceParams getItemsToReduceParams)
 422		{
 423			var scheduledReductionsByViewAndLevelAndReduceKey = tableStorage.ScheduledReductions.GetIndex(Tables.ScheduledReductions.Indices.ByViewAndLevelAndReduceKey);
 424            var deleter = new ScheduledReductionDeleter(getItemsToReduceParams.ItemsToDelete, o =>
 425            {
 426                var json = o as RavenJObject;
 427                if (json == null) 
 428                    return null;
 429
 430                var etag = Etag.Parse(json.Value<byte[]>("etag"));
 431                return etag.ToString();
 432            });
 433
 434			var seenLocally = new HashSet<Tuple<string, int>>();
 435			foreach (var reduceKey in getItemsToReduceParams.ReduceKeys.ToArray())
 436			{
 437			    var reduceKeyHash = HashKey(reduceKey);
 438                var viewAndLevelAndReduceKey = CreateKey(getItemsToReduceParams.Index, getItemsToReduceParams.Level, reduceKey, reduceKeyHash);
 439				using (var iterator = scheduledReductionsByViewAndLevelAndReduceKey.MultiRead(Snapshot, viewAndLevelAndReduceKey))
 440				{
 441					if (!iterator.Seek(Slice.BeforeAllKeys))
 442						continue;
 443
 444					do
 445					{
 446						if (getItemsToReduceParams.Take <= 0)
 447							break;
 448
 449						ushort version;
 450						var value = LoadJson(tableStorage.ScheduledReductions, iterator.CurrentKey, writeBatch.Value, out version);
 451
 452						var reduceKeyFromDb = value.Value<string>("reduceKey");
 453
 454						var bucket = value.Value<int>("bucket");
 455						var rowKey = Tuple.Create(reduceKeyFromDb, bucket);
 456					    var thisIsNewScheduledReductionRow = deleter.Delete(iterator.CurrentKey, value);
 457						var neverSeenThisKeyAndBucket = getItemsToReduceParams.ItemsAlreadySeen.Add(rowKey);
 458						if (thisIsNewScheduledReductionRow || neverSeenThisKeyAndBucket)
 459						{
 460							if (seenLocally.Add(rowKey))
 461							{
 462								foreach (var mappedResultInfo in GetResultsForBucket(getItemsToReduceParams.Index, getItemsToReduceParams.Level, reduceKeyFromDb, bucket, getItemsToReduceParams.LoadData))
 463								{
 464									getItemsToReduceParams.Take--;
 465									yield return mappedResultInfo;
 466								}
 467							}
 468						}
 469
 470						if (getItemsToReduceParams.Take <= 0)
 471							yield break;
 472					}
 473					while (iterator.MoveNext());
 474				}
 475
 476				getItemsToReduceParams.ReduceKeys.Remove(reduceKey);
 477
 478				if (getItemsToReduceParams.Take <= 0)
 479                    yield break;
 480			}
 481		}
 482
 483		private IEnumerable<MappedResultInfo> GetResultsForBucket(int view, int level, string reduceKey, int bucket, bool loadData)
 484		{
 485			switch (level)
 486			{
 487				case 0:
 488					return GetMappedResultsForBucket(view, reduceKey, bucket, loadData);
 489				case 1:
 490				case 2:
 491					return GetReducedResultsForBucket(view, reduceKey, level, bucket, loadData);
 492				default:
 493					throw new ArgumentException("Invalid level: " + level);
 494			}
 495		}
 496
 497		private IEnumerable<MappedResultInfo> GetReducedResultsForBucket(int view, string reduceKey, int level, int bucket, bool loadData)
 498		{
 499		    var reduceKeyHash = HashKey(reduceKey);
 500            var viewAndReduceKeyAndLevelAndBucket = CreateKey(view, reduceKey, reduceKeyHash, level, bucket);
 501
 502			var reduceResultsByViewAndReduceKeyAndLevelAndBucket = tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.ByViewAndReduceKeyAndLevelAndBucket);
 503			var reduceResultsData = tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.Data);
 504			using (var iterator = reduceResultsByViewAndReduceKeyAndLevelAndBucket.MultiRead(Snapshot, viewAndReduceKeyAndLevelAndBucket))
 505			{
 506				if (!iterator.Seek(Slice.BeforeAllKeys))
 507				{
 508					yield return new MappedResultInfo
 509								 {
 510									 Bucket = bucket,
 511									 ReduceKey = reduceKey
 512								 };
 513
 514					yield break;
 515				}
 516
 517				do
 518				{
 519					ushort version;
 520					var value = LoadJson(tableStorage.ReduceResults, iterator.CurrentKey, writeBatch.Value, out version);
 521					var size = tableStorage.ReduceResults.GetDataSize(Snapshot, iterator.CurrentKey);
 522
 523					yield return new MappedResultInfo
 524					{
 525						ReduceKey = value.Value<string>("reduceKey"),
 526						Etag = Etag.Parse(value.Value<byte[]>("etag")),
 527						Timestamp = value.Value<DateTime>("timestamp"),
 528						Bucket = value.Value<int>("bucket"),
 529						Source = null,
 530						Size = size,
 531						Data = loadData ? LoadMappedResult(iterator.CurrentKey, value, reduceResultsData) : null
 532					};
 533				}
 534				while (iterator.MoveNext());
 535			}
 536		}
 537
 538		private IEnumerable<MappedResultInfo> GetMappedResultsForBucket(int view, string reduceKey, int bucket, bool loadData)
 539		{
 540		    var reduceKeyHash = HashKey(reduceKey);
 541            var viewAndReduceKeyAndSourceBucket = CreateKey(view, reduceKey, reduceKeyHash, bucket);
 542
 543			var mappedResultsByViewAndReduceKeyAndSourceBucket = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByViewAndReduceKeyAndSourceBucket);
 544			var mappedResultsData = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.Data);
 545
 546			using (var iterator = mappedResultsByViewAndReduceKeyAndSourceBucket.MultiRead(Snapshot, viewAndReduceKeyAndSourceBucket))
 547			{
 548				if (!iterator.Seek(Slice.BeforeAllKeys))
 549				{
 550					yield return new MappedResultInfo
 551					{
 552						Bucket = bucket,
 553						ReduceKey = reduceKey
 554					};
 555
 556					yield break;
 557				}
 558
 559				do
 560				{
 561					ushort version;
 562					var value = LoadJson(tableStorage.MappedResults, iterator.CurrentKey, writeBatch.Value, out version);
 563					var size = tableStorage.MappedResults.GetDataSize(Snapshot, iterator.CurrentKey);
 564
 565					yield return new MappedResultInfo
 566					{
 567						ReduceKey = value.Value<string>("reduceKey"),
 568						Etag = Etag.Parse(value.Value<byte[]>("etag")),
 569						Timestamp = value.Value<DateTime>("timestamp"),
 570						Bucket = value.Value<int>("bucket"),
 571						Source = null,
 572						Size = size,
 573						Data = loadData ? LoadMappedResult(iterator.CurrentKey, value, mappedResultsData) : null
 574					};
 575				}
 576				while (iterator.MoveNext());
 577			}
 578		}
 579
 580		public ScheduledReductionInfo DeleteScheduledReduction(IEnumerable<object> itemsToDelete)
 581		{
 582			if (itemsToDelete == null)
 583				return null;
 584
 585			var result = new ScheduledReductionInfo();
 586			var hasResult = false;
 587			var currentEtag = Etag.Empty;
 588			foreach (RavenJToken token in itemsToDelete)
 589			{
 590				var etag = Etag.Parse(token.Value<byte[]>("etag"));
 591				var etagAsString = etag.ToString();
 592
 593				ushort version;
 594				var value = LoadJson(tableStorage.ScheduledReductions, etagAsString, writeBatch.Value, out version);
 595				if (value == null)
 596					continue;
 597
 598				if (etag.CompareTo(currentEtag) > 0)
 599				{
 600					hasResult = true;
 601					result.Etag = etag;
 602					result.Timestamp = value.Value<DateTime>("timestamp");
 603				}
 604
 605				var view = value.Value<int>("view");
 606				var level = value.Value<int>("level");
 607				var reduceKey = value.Value<string>("reduceKey");
 608
 609				DeleteScheduledReduction(etagAsString, view, level, reduceKey);
 610			}
 611
 612			return hasResult ? result : null;
 613		}
 614
 615		public void DeleteScheduledReduction(int view, int level, string reduceKey)
 616		{
 617		    var reduceKeyHash = HashKey(reduceKey);
 618			var scheduledReductionsByViewAndLevelAndReduceKey = tableStorage.ScheduledReductions.GetIndex(Tables.ScheduledReductions.Indices.ByViewAndLevelAndReduceKey);
 619            using (var iterator = scheduledReductionsByViewAndLevelAndReduceKey.MultiRead(Snapshot, CreateKey(view, level, reduceKey, reduceKeyHash)))
 620			{
 621				if (!iterator.Seek(Slice.BeforeAllKeys))
 622					return;
 623
 624				do
 625				{
 626					var id = iterator.CurrentKey;
 627					DeleteScheduledReduction(id, view, level, reduceKey);
 628				}
 629				while (iterator.MoveNext());
 630			}
 631		}
 632
 633		public void PutReducedResult(int view, string reduceKey, int level, int sourceBucket, int bucket, RavenJObject data)
 634		{
 635			var reduceResultsByViewAndReduceKeyAndLevelAndSourceBucket =
 636				tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.ByViewAndReduceKeyAndLevelAndSourceBucket);
 637			var reduceResultsByViewAndReduceKeyAndLevelAndBucket =
 638				tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.ByViewAndReduceKeyAndLevelAndBucket);
 639			var reduceResultsByViewAndReduceKeyAndLevel =
 640				tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.ByViewAndReduceKeyAndLevel);
 641			var reduceResultsByView =
 642				tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.ByView);
 643			var reduceResultsData = tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.Data);
 644
 645            var ms = CreateStream();
 646			using (
 647				var stream = documentCodecs.Aggregate((Stream) new UndisposableStream(ms),
 648					(ds, codec) => codec.Value.Encode(reduceKey, data, null, ds)))
 649			{
 650				data.WriteTo(stream);
 651				stream.Flush();
 652			}
 653
 654			var id = generator.CreateSequentialUuid(UuidType.MappedResults);
 655			var idAsString = id.ToString();
 656		    var reduceKeyHash = HashKey(reduceKey);
 657
 658			tableStorage.ReduceResults.Add(
 659				writeBatch.Value,
 660				idAsString,
 661				new RavenJObject
 662				{
 663					{ "view", view },
 664					{ "etag", id.ToByteArray() },
 665					{ "reduceKey", reduceKey },
 666					{ "level", level },
 667					{ "sourceBucket", sourceBucket },
 668					{ "bucket", bucket },
 669					{ "timestamp", SystemTime.UtcNow }
 670				},
 671				0);
 672
 673			ms.Position = 0;
 674			reduceResultsData.Add(writeBatch.Value, idAsString, ms, 0);
 675
 676            var viewAndReduceKeyAndLevelAndSourceBucket = CreateKey(view, reduceKey, reduceKeyHash, level, sourceBucket);
 677            var viewAndReduceKeyAndLevel = CreateKey(view, reduceKey, reduceKeyHash, level);
 678            var viewAndReduceKeyAndLevelAndBucket = CreateKey(view, reduceKey, reduceKeyHash, level, bucket);
 679
 680			reduceResultsByViewAndReduceKeyAndLevelAndSourceBucket.MultiAdd(writeBatch.Value, viewAndReduceKeyAndLevelAndSourceBucket, idAsString);
 681			reduceResultsByViewAndReduceKeyAndLevel.MultiAdd(writeBatch.Value, viewAndReduceKeyAndLevel, idAsString);
 682			reduceResultsByViewAndReduceKeyAndLevelAndBucket.MultiAdd(writeBatch.Value, viewAndReduceKeyAndLevelAndBucket, idAsString);
 683			reduceResultsByView.MultiAdd(writeBatch.Value, CreateKey(view), idAsString);
 684		}
 685
 686		public void RemoveReduceResults(int view, int level, string reduceKey, int sourceBucket)
 687		{
 688		    var reduceKeyHash = HashKey(reduceKey);
 689            var viewAndReduceKeyAndLevelAndSourceBucket = CreateKey(view, reduceKey, reduceKeyHash, level, sourceBucket);
 690			var reduceResultsByViewAndReduceKeyAndLevelAndSourceBucket =
 691				tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.ByViewAndReduceKeyAndLevelAndSourceBucket);
 692
 693			using (var iterator = reduceResultsByViewAndReduceKeyAndLevelAndSourceBucket.MultiRead(Snapshot, viewAndReduceKeyAndLevelAndSourceBucket))
 694			{
 695				if (!iterator.Seek(Slice.BeforeAllKeys))
 696					return;
 697
 698				do
 699				{
 700					RemoveReduceResult(iterator.CurrentKey.Clone());
 701				}
 702				while (iterator.MoveNext());
 703			}
 704		}
 705
 706		public IEnumerable<ReduceTypePerKey> GetReduceTypesPerKeys(int view, int take, int limitOfItemsToReduceInSingleStep)
 707		{
 708			if (take <= 0)
 709				take = 1;
 710
 711			var allKeysToReduce = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
 712
 713			var key = CreateKey(view);
 714			var scheduledReductionsByView = tableStorage.ScheduledReductions.GetIndex(Tables.ScheduledReductions.Indices.ByView);
 715            using (var iterator = scheduledReductionsByView.MultiRead(Snapshot, key))
 716			{
 717				if (!iterator.Seek(Slice.BeforeAllKeys))
 718					yield break;
 719
 720                var processedItems = 0;
 721
 722				do
 723				{
 724					ushort version;
 725					var value = LoadJson(tableStorage.ScheduledReductions, iterator.CurrentKey, writeBatch.Value, out version);
 726
 727					allKeysToReduce.Add(value.Value<string>("reduceKey"));
 728                    processedItems++;
 729				}
 730				while (iterator.MoveNext() && processedItems < take);
 731			}
 732
 733            foreach (var reduceKey in allKeysToReduce)
 734            {
 735                var count = GetNumberOfMappedItemsPerReduceKey(view, reduceKey);
 736                var reduceType = count >= limitOfItemsToReduceInSingleStep ? ReduceType.MultiStep : ReduceType.SingleStep;
 737                yield return new ReduceTypePerKey(reduceKey, reduceType);
 738            }
 739		}
 740
 741		private int GetNumberOfMappedItemsPerReduceKey(int view, string reduceKey)
 742		{
 743		    var reduceKeyHash = HashKey(reduceKey);
 744            var key = CreateKey(view, reduceKey, reduceKeyHash);
 745
 746			ushort version;
 747			var value = LoadJson(tableStorage.ReduceKeyCounts, key, writeBatch.Value, out version);
 748			if (value == null)
 749				return 0;
 750
 751			return value.Value<int>("mappedItemsCount");
 752		}
 753
 754		public void UpdatePerformedReduceType(int view, string reduceKey, ReduceType reduceType)
 755		{
 756            var reduceKeyHash = HashKey(reduceKey);
 757			var key = CreateKey(view, reduceKey, reduceKeyHash);
 758			var version = tableStorage.ReduceKeyTypes.ReadVersion(Snapshot, key);
 759
 760			AddReduceKeyType(key, view, reduceKey, reduceType, version);
 761		}
 762
 763		private void DeleteReduceKeyCount(string key, int view, ushort? expectedVersion)
 764		{
 765			var reduceKeyCountsByView = tableStorage.ReduceKeyCounts.GetIndex(Tables.ReduceKeyCounts.Indices.ByView);
 766
 767			tableStorage.ReduceKeyCounts.Delete(writeBatch.Value, key, expectedVersion);
 768			reduceKeyCountsByView.MultiDelete(writeBatch.Value, CreateKey(view), key);
 769		}
 770
 771		private void DeleteReduceKeyType(string key, int view, ushort? expectedVersion)
 772		{
 773			var reduceKeyTypesByView = tableStorage.ReduceKeyTypes.GetIndex(Tables.ReduceKeyTypes.Indices.ByView);
 774
 775			tableStorage.ReduceKeyTypes.Delete(writeBatch.Value, key, expectedVersion);
 776			reduceKeyTypesByView.MultiDelete(writeBatch.Value, CreateKey(view), key);
 777		}
 778
 779		private void AddReduceKeyCount(string key, int view, string reduceKey, int count, ushort? expectedVersion)
 780		{
 781			var reduceKeyCountsByView = tableStorage.ReduceKeyCounts.GetIndex(Tables.ReduceKeyCounts.Indices.ByView);
 782
 783			tableStorage.ReduceKeyCounts.Add(
 784						writeBatch.Value,
 785						key,
 786						new RavenJObject
 787						{
 788							{ "view", view },
 789							{ "reduceKey", reduceKey },
 790							{ "mappedItemsCount", count }
 791						}, expectedVersion);
 792
 793			reduceKeyCountsByView.MultiAdd(writeBatch.Value, CreateKey(view), key);
 794		}
 795
 796		private void AddReduceKeyType(string key, int view, string reduceKey, ReduceType status, ushort? expectedVersion)
 797		{
 798			var reduceKeyTypesByView = tableStorage.ReduceKeyTypes.GetIndex(Tables.ReduceKeyTypes.Indices.ByView);
 799
 800			tableStorage.ReduceKeyTypes.Add(
 801						writeBatch.Value,
 802						key,
 803						new RavenJObject
 804						{
 805							{ "view", view },
 806							{ "reduceKey", reduceKey },
 807							{ "reduceType", (int)status }
 808						}, expectedVersion);
 809
 810			reduceKeyTypesByView.MultiAdd(writeBatch.Value, CreateKey(view), key);
 811		}
 812
 813		public ReduceType GetLastPerformedReduceType(int view, string reduceKey)
 814		{
 815            var reduceKeyHash = HashKey(reduceKey);
 816			var key = CreateKey(view, reduceKey, reduceKeyHash);
 817
 818			ushort version;
 819			var value = LoadJson(tableStorage.ReduceKeyTypes, key, writeBatch.Value, out version);
 820			if (value == null)
 821				return ReduceType.None;
 822
 823			return (ReduceType)value.Value<int>("reduceType");
 824		}
 825
 826		public IEnumerable<int> GetMappedBuckets(int view, string reduceKey)
 827		{
 828            var reduceKeyHash = HashKey(reduceKey);
 829			var viewAndReduceKey = CreateKey(view, reduceKey, reduceKeyHash);
 830			var mappedResultsByViewAndReduceKey = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByViewAndReduceKey);
 831
 832			using (var iterator = mappedResultsByViewAndReduceKey.MultiRead(Snapshot, viewAndReduceKey))
 833			{
 834				if (!iterator.Seek(Slice.BeforeAllKeys))
 835					yield break;
 836
 837				do
 838				{
 839					ushort version;
 840					var value = LoadJson(tableStorage.MappedResults, iterator.CurrentKey, writeBatch.Value, out version);
 841
 842					yield return value.Value<int>("bucket");
 843				}
 844				while (iterator.MoveNext());
 845			}
 846		}
 847
 848		public IEnumerable<MappedResultInfo> GetMappedResults(int view, IEnumerable<string> keysToReduce, bool loadData)
 849		{
 850			var mappedResultsByViewAndReduceKey = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByViewAndReduceKey);
 851			var mappedResultsData = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.Data);
 852
 853			foreach (var reduceKey in keysToReduce)
 854			{
 855                var reduceKeyHash = HashKey(reduceKey);
 856                var viewAndReduceKey = CreateKey(view, reduceKey, reduceKeyHash);
 857				using (var iterator = mappedResultsByViewAndReduceKey.MultiRead(Snapshot, viewAndReduceKey))
 858				{
 859					if (!iterator.Seek(Slice.BeforeAllKeys))
 860						continue;
 861
 862					do
 863					{
 864						ushort version;
 865						var value = LoadJson(tableStorage.MappedResults, iterator.CurrentKey, writeBatch.Value, out version);
 866						var size = tableStorage.MappedResults.GetDataSize(Snapshot, iterator.CurrentKey);
 867
 868						yield return new MappedResultInfo
 869						{
 870							Bucket = value.Value<int>("bucket"),
 871							ReduceKey = value.Value<string>("reduceKey"),
 872							Etag = Etag.Parse(value.Value<byte[]>("etag")),
 873							Timestamp = value.Value<DateTime>("timestamp"),
 874							Data = loadData ? LoadMappedResult(iterator.CurrentKey, value, mappedResultsData) : null,
 875							Size = size
 876						};
 877					}
 878					while (iterator.MoveNext());
 879				}
 880			}
 881		}
 882
 883		private RavenJObject LoadMappedResult(Slice key, RavenJObject value, Index dataIndex)
 884		{
 885			var reduceKey = value.Value<string>("reduceKey");
 886
 887			var read = dataIndex.Read(Snapshot, key, writeBatch.Value);
 888			if (read == null)
 889				return null;
 890
 891			using (var readerStream = read.Reader.AsStream())
 892			{
 893				using (var stream = documentCodecs.Aggregate(readerStream, (ds, codec) => codec.Decode(reduceKey, null, ds)))
 894					return stream.ToJObject();
 895			}
 896		}
 897
 898		public IEnumerable<ReduceTypePerKey> GetReduceKeysAndTypes(int view, int start, int take)
 899		{
 900			var reduceKeyTypesByView = tableStorage.ReduceKeyTypes.GetIndex(Tables.ReduceKeyTypes.Indices.ByView);
 901			using (var iterator = reduceKeyTypesByView.MultiRead(Snapshot, CreateKey(view)))
 902			{
 903				if (!iterator.Seek(Slice.BeforeAllKeys) || !iterator.Skip(start))
 904					yield break;
 905
 906				var count = 0;
 907				do
 908				{
 909					ushort version;
 910					var value = LoadJson(tableStorage.ReduceKeyTypes, iterator.CurrentKey, writeBatch.Value, out version);
 911
 912					yield return new ReduceTypePerKey(value.Value<string>("reduceKey"), (ReduceType)value.Value<int>("reduceType"));
 913
 914					count++;
 915				}
 916				while (iterator.MoveNext() && count < take);
 917			}
 918		}
 919
 920		public void DeleteScheduledReductionForView(int view)
 921		{
 922			var scheduledReductionsByView = tableStorage.ScheduledReductions.GetIndex(Tables.ScheduledReductions.Indices.ByView);
 923
 924			using (var iterator = scheduledReductionsByView.MultiRead(Snapshot, CreateKey(view)))
 925			{
 926				if (!iterator.Seek(Slice.BeforeAllKeys))
 927					return;
 928
 929				do
 930				{
 931					var id = iterator.CurrentKey.Clone();
 932
 933					ushort version;
 934					var value = LoadJson(tableStorage.ScheduledReductions, id, writeBatch.Value, out version);
 935					if (value == null)
 936						continue;
 937
 938					var v = value.Value<int>("view");
 939					var level = value.Value<int>("level");
 940					var reduceKey = value.Value<string>("reduceKey");
 941
 942					DeleteScheduledReduction(id, v, level, reduceKey);
 943				}
 944				while (iterator.MoveNext());
 945			}
 946		}
 947
 948		public void RemoveReduceResultsForView(int view)
 949		{
 950			var reduceResultsByView =
 951				tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.ByView);
 952
 953			using (var iterator = reduceResultsByView.MultiRead(Snapshot, CreateKey(view)))
 954			{
 955				if (!iterator.Seek(Slice.BeforeAllKeys))
 956					return;
 957
 958				do
 959				{
 960					var id = iterator.CurrentKey.Clone();
 961
 962					RemoveReduceResult(id);
 963				}
 964				while (iterator.MoveNext());
 965			}
 966		}
 967
 968		private void DeleteScheduledReduction(Slice id, int view, int level, string reduceKey)
 969		{
 970		    var reduceKeyHash = HashKey(reduceKey);
 971
 972			var scheduledReductionsByView = tableStorage.ScheduledReductions.GetIndex(Tables.ScheduledReductions.Indices.ByView);
 973			var scheduledReductionsByViewAndLevelAndReduceKey = tableStorage.ScheduledReductions.GetIndex(Tables.ScheduledReductions.Indices.ByViewAndLevelAndReduceKey);
 974
 975			tableStorage.ScheduledReductions.Delete(writeBatch.Value, id);
 976			scheduledReductionsByView.MultiDelete(writeBatch.Value, CreateKey(view), id);
 977			scheduledReductionsByViewAndLevelAndReduceKey.MultiDelete(writeBatch.Value, CreateKey(view, level, reduceKey, reduceKeyHash), id);
 978		}
 979
 980		private void DeleteMappedResult(Slice id, int view, string documentId, string reduceKey, string bucket)
 981		{
 982		    var reduceKeyHash = HashKey(reduceKey);
 983			var viewAndDocumentId = CreateKey(view, documentId);
 984            var viewAndReduceKey = CreateKey(view, reduceKey, reduceKeyHash);
 985            var viewAndReduceKeyAndSourceBucket = CreateKey(view, reduceKey, reduceKeyHash, bucket);
 986			var mappedResultsByViewAndDocumentId = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByViewAndDocumentId);
 987			var mappedResultsByView = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByView);
 988			var mappedResultsByViewAndReduceKey = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByViewAndReduceKey);
 989			var mappedResultsByViewAndReduceKeyAndSourceBucket = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.ByViewAndReduceKeyAndSourceBucket);
 990			var mappedResultsData = tableStorage.MappedResults.GetIndex(Tables.MappedResults.Indices.Data);
 991
 992			tableStorage.MappedResults.Delete(writeBatch.Value, id);
 993			mappedResultsByViewAndDocumentId.MultiDelete(writeBatch.Value, viewAndDocumentId, id);
 994			mappedResultsByView.MultiDelete(writeBatch.Value, CreateKey(view), id);
 995			mappedResultsByViewAndReduceKey.MultiDelete(writeBatch.Value, viewAndReduceKey, id);
 996			mappedResultsByViewAndReduceKeyAndSourceBucket.MultiDelete(writeBatch.Value, viewAndReduceKeyAndSourceBucket, id);
 997			mappedResultsData.Delete(writeBatch.Value, id);
 998		}
 999
1000		private void RemoveReduceResult(Slice id)
1001		{
1002			ushort version;
1003			var value = LoadJson(tableStorage.ReduceResults, id, writeBatch.Value, out version);
1004
1005			var view = value.Value<string>("view");
1006			var reduceKey = value.Value<string>("reduceKey");
1007			var level = value.Value<int>("level");
1008			var bucket = value.Value<int>("bucket");
1009			var sourceBucket = value.Value<int>("sourceBucket");
1010		    var reduceKeyHash = HashKey(reduceKey);
1011
1012            var viewAndReduceKeyAndLevelAndSourceBucket = CreateKey(view, reduceKey, reduceKeyHash, level, sourceBucket);
1013            var viewAndReduceKeyAndLevel = CreateKey(view, reduceKey, reduceKeyHash, level);
1014            var viewAndReduceKeyAndLevelAndBucket = CreateKey(view, reduceKey, reduceKeyHash, level, bucket);
1015
1016			var reduceResultsByViewAndReduceKeyAndLevelAndSourceBucket =
1017				tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.ByViewAndReduceKeyAndLevelAndSourceBucket);
1018			var reduceResultsByViewAndReduceKeyAndLevel =
1019				tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.ByViewAndReduceKeyAndLevel);
1020			var reduceResultsByViewAndReduceKeyAndLevelAndBucket =
1021				tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.ByViewAndReduceKeyAndLevelAndBucket);
1022			var reduceResultsByView =
1023				tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.ByView);
1024			var reduceResultsData = tableStorage.ReduceResults.GetIndex(Tables.ReduceResults.Indices.Data);
1025
1026			tableStorage.ReduceResults.Delete(writeBatch.Value, id);
1027			reduceResultsByViewAndReduceKeyAndLevelAndSourceBucket.MultiDelete(writeBatch.Value, viewAndReduceKeyAndLevelAndSourceBucket, id);
1028			reduceResultsByViewAndReduceKeyAndLevel.MultiDelete(writeBatch.Value, viewAndReduceKeyAndLevel, id);
1029			reduceResultsByViewAndReduceKeyAndLevelAndBucket.MultiDelete(writeBatch.Value, viewAndReduceKeyAndLevelAndBucket, id);
1030			reduceResultsByView.MultiDelete(writeBatch.Value, CreateKey(view), id);
1031			reduceResultsData.Delete(writeBatch.Value, id);
1032		}
1033
1034        private static string HashKey(string key)
1035        {
1036            return Convert.ToBase64String(Encryptor.Current.Hash.Compute16(Encoding.UTF8.GetBytes(key)));
1037        }
1038	}
1039
1040    public class ScheduledReductionDeleter
1041    {
1042        private readonly ConcurrentSet<object> innerSet;
1043
1044        private readonly IDictionary<Slice, object> state = new Dictionary<Slice, object>(new SliceEqualityComparer());
1045
1046        public ScheduledReductionDeleter(ConcurrentSet<object> set, Func<object, Slice> extractKey)
1047        {
1048            innerSet = set;
1049
1050            InitializeState(set, extractKey);
1051        }
1052
1053        private void InitializeState(IEnumerable<object> set, Func<object, Slice> extractKey)
1054        {
1055            foreach (var item in set)
1056            {
1057                var key = extractKey(item);
1058                if (key == null)
1059                    continue;
1060
1061                state.Add(key, null);
1062            }
1063        }
1064
1065        public bool Delete(Slice key, object value)
1066        {
1067            if (state.ContainsKey(key))
1068                return false;
1069
1070            state.Add(key, null);
1071            innerSet.Add(value);
1072
1073            return true;
1074        }
1075    }
1076}