PageRenderTime 68ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://github.com/nwendel/ravendb
C# | 695 lines | 548 code | 145 blank | 2 comment | 119 complexity | 6de3c7ab17dad266510349a1985baadb MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, BSD-3-Clause, CC-BY-SA-3.0
  1. using Raven.Abstractions;
  2. using Raven.Abstractions.Data;
  3. using Raven.Abstractions.Exceptions;
  4. using Raven.Abstractions.Extensions;
  5. using Raven.Abstractions.Logging;
  6. using Raven.Abstractions.MEF;
  7. using Raven.Abstractions.Util;
  8. using Raven.Abstractions.Util.Streams;
  9. using Raven.Database.Impl;
  10. using Raven.Database.Plugins;
  11. using Raven.Database.Storage.Voron.Impl;
  12. using Raven.Json.Linq;
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Diagnostics;
  16. using System.IO;
  17. using System.Linq;
  18. using System.Text;
  19. using System.Threading;
  20. using Voron;
  21. using Voron.Impl;
  22. using Constants = Raven.Abstractions.Data.Constants;
  23. namespace Raven.Database.Storage.Voron.StorageActions
  24. {
  25. public class DocumentsStorageActions : StorageActionsBase, IDocumentStorageActions
  26. {
  27. private readonly Reference<WriteBatch> writeBatch;
  28. private readonly IUuidGenerator uuidGenerator;
  29. private readonly OrderedPartCollection<AbstractDocumentCodec> documentCodecs;
  30. private readonly IDocumentCacher documentCacher;
  31. private static readonly ILog logger = LogManager.GetCurrentClassLogger();
  32. private readonly Dictionary<Etag, Etag> etagTouches = new Dictionary<Etag, Etag>();
  33. private readonly TableStorage tableStorage;
  34. private readonly Index metadataIndex;
  35. public DocumentsStorageActions(IUuidGenerator uuidGenerator,
  36. OrderedPartCollection<AbstractDocumentCodec> documentCodecs,
  37. IDocumentCacher documentCacher,
  38. Reference<WriteBatch> writeBatch,
  39. Reference<SnapshotReader> snapshot,
  40. TableStorage tableStorage,
  41. IBufferPool bufferPool)
  42. : base(snapshot, bufferPool)
  43. {
  44. this.uuidGenerator = uuidGenerator;
  45. this.documentCodecs = documentCodecs;
  46. this.documentCacher = documentCacher;
  47. this.writeBatch = writeBatch;
  48. this.tableStorage = tableStorage;
  49. metadataIndex = tableStorage.Documents.GetIndex(Tables.Documents.Indices.Metadata);
  50. }
  51. public IEnumerable<JsonDocument> GetDocumentsByReverseUpdateOrder(int start, int take)
  52. {
  53. if (start < 0)
  54. throw new ArgumentException("must have zero or positive value", "start");
  55. if (take < 0)
  56. throw new ArgumentException("must have zero or positive value", "take");
  57. if (take == 0) yield break;
  58. using (var iterator = tableStorage.Documents.GetIndex(Tables.Documents.Indices.KeyByEtag)
  59. .Iterate(Snapshot, writeBatch.Value))
  60. {
  61. int fetchedDocumentCount = 0;
  62. if (!iterator.Seek(Slice.AfterAllKeys))
  63. yield break;
  64. if (!iterator.Skip(-start))
  65. yield break;
  66. do
  67. {
  68. if (iterator.CurrentKey == null || iterator.CurrentKey.Equals(Slice.Empty))
  69. yield break;
  70. var key = GetKeyFromCurrent(iterator);
  71. var document = DocumentByKey(key, null);
  72. if (document == null) //precaution - should never be true
  73. {
  74. throw new InvalidDataException(string.Format("Possible data corruption - the key = '{0}' was found in the documents indice, but matching document was not found.", key));
  75. }
  76. yield return document;
  77. fetchedDocumentCount++;
  78. } while (iterator.MovePrev() && fetchedDocumentCount < take);
  79. }
  80. }
  81. public IEnumerable<KeyValuePair<string, Etag>> GetDocumentEtagsFromKeyByEtagIndice()
  82. {
  83. using (var iterator = tableStorage.Documents.GetIndex(Tables.Documents.Indices.KeyByEtag)
  84. .Iterate(Snapshot, writeBatch.Value))
  85. {
  86. if (!iterator.Seek(Slice.AfterAllKeys))
  87. yield break;
  88. do
  89. {
  90. if (iterator.CurrentKey == null || iterator.CurrentKey.Equals(Slice.Empty))
  91. continue;
  92. var key = GetKeyFromCurrent(iterator);
  93. var etag = Etag.Parse(iterator.CurrentKey.ToString());
  94. yield return new KeyValuePair<string, Etag>(key,etag);
  95. } while (iterator.MovePrev());
  96. }
  97. }
  98. public IEnumerable<KeyValuePair<string, Etag>> GetDocumentEtagsFromMetadata()
  99. {
  100. using (var iterator = tableStorage.Documents.GetIndex(Tables.Documents.Indices.KeyByEtag)
  101. .Iterate(Snapshot, writeBatch.Value))
  102. {
  103. if (!iterator.Seek(Slice.AfterAllKeys))
  104. yield break;
  105. do
  106. {
  107. if (iterator.CurrentKey == null || iterator.CurrentKey.Equals(Slice.Empty))
  108. continue;
  109. var key = GetKeyFromCurrent(iterator);
  110. var documentMetadata = DocumentMetadataByKey(key, null);
  111. yield return new KeyValuePair<string, Etag>(key, documentMetadata.Etag);
  112. } while (iterator.MovePrev());
  113. }
  114. }
  115. public IEnumerable<JsonDocument> GetDocumentsAfter(Etag etag, int take, CancellationToken cancellationToken, long? maxSize = null, Etag untilEtag = null, TimeSpan? timeout = null)
  116. {
  117. if (take < 0)
  118. throw new ArgumentException("must have zero or positive value", "take");
  119. if (take == 0) yield break;
  120. if (string.IsNullOrEmpty(etag))
  121. throw new ArgumentNullException("etag");
  122. Stopwatch duration = null;
  123. if (timeout != null)
  124. duration = Stopwatch.StartNew();
  125. using (var iterator = tableStorage.Documents.GetIndex(Tables.Documents.Indices.KeyByEtag)
  126. .Iterate(Snapshot, writeBatch.Value))
  127. {
  128. if (!iterator.Seek(Slice.BeforeAllKeys))
  129. {
  130. yield break;
  131. }
  132. long fetchedDocumentTotalSize = 0;
  133. int fetchedDocumentCount = 0;
  134. do
  135. {
  136. cancellationToken.ThrowIfCancellationRequested();
  137. if (iterator.CurrentKey == null || iterator.CurrentKey.Equals(Slice.Empty))
  138. yield break;
  139. var docEtag = Etag.Parse(iterator.CurrentKey.ToString());
  140. if (!EtagUtil.IsGreaterThan(docEtag, etag)) continue;
  141. if (untilEtag != null && fetchedDocumentCount > 0)
  142. {
  143. if (EtagUtil.IsGreaterThan(docEtag, untilEtag))
  144. yield break;
  145. }
  146. var key = GetKeyFromCurrent(iterator);
  147. var document = DocumentByKey(key, null);
  148. if (document == null) //precaution - should never be true
  149. {
  150. throw new InvalidDataException(string.Format("Data corruption - the key = '{0}' was found in the documents indice, but matching document was not found", key));
  151. }
  152. if (!document.Etag.Equals(docEtag))
  153. {
  154. throw new InvalidDataException(string.Format("Data corruption - the etag for key ='{0}' is different between document and its indice",key));
  155. }
  156. fetchedDocumentTotalSize += document.SerializedSizeOnDisk;
  157. fetchedDocumentCount++;
  158. if (maxSize.HasValue && fetchedDocumentTotalSize >= maxSize)
  159. {
  160. yield return document;
  161. yield break;
  162. }
  163. yield return document;
  164. if (timeout != null)
  165. {
  166. if (duration.Elapsed > timeout.Value)
  167. yield break;
  168. }
  169. } while (iterator.MoveNext() && fetchedDocumentCount < take);
  170. }
  171. }
  172. private static string GetKeyFromCurrent(global::Voron.Trees.IIterator iterator)
  173. {
  174. string key;
  175. using (var currentDataStream = iterator.CreateReaderForCurrent().AsStream())
  176. {
  177. var keyBytes = currentDataStream.ReadData();
  178. key = Encoding.UTF8.GetString(keyBytes);
  179. }
  180. return key;
  181. }
  182. public IEnumerable<JsonDocument> GetDocumentsWithIdStartingWith(string idPrefix, int start, int take, string skipAfter)
  183. {
  184. if (string.IsNullOrEmpty(idPrefix))
  185. throw new ArgumentNullException("idPrefix");
  186. if (start < 0)
  187. throw new ArgumentException("must have zero or positive value", "start");
  188. if (take < 0)
  189. throw new ArgumentException("must have zero or positive value", "take");
  190. if (take == 0)
  191. yield break;
  192. using (var iterator = tableStorage.Documents.Iterate(Snapshot, writeBatch.Value))
  193. {
  194. iterator.RequiredPrefix = idPrefix.ToLowerInvariant();
  195. var seekStart = skipAfter == null ? iterator.RequiredPrefix : skipAfter.ToLowerInvariant();
  196. if (iterator.Seek(seekStart) == false || !iterator.Skip(start))
  197. yield break;
  198. if (skipAfter != null && !iterator.MoveNext())
  199. yield break; // move to the _next_ one
  200. var fetchedDocumentCount = 0;
  201. do
  202. {
  203. var key = iterator.CurrentKey.ToString();
  204. var fetchedDocument = DocumentByKey(key, null);
  205. if (fetchedDocument == null) continue;
  206. fetchedDocumentCount++;
  207. yield return fetchedDocument;
  208. } while (iterator.MoveNext() && fetchedDocumentCount < take);
  209. }
  210. }
  211. public long GetDocumentsCount()
  212. {
  213. return tableStorage.GetEntriesCount(tableStorage.Documents);
  214. }
  215. public JsonDocument DocumentByKey(string key, TransactionInformation transactionInformation)
  216. {
  217. if (string.IsNullOrEmpty(key))
  218. {
  219. logger.Debug("Document with empty key was not found");
  220. return null;
  221. }
  222. var lowerKey = CreateKey(key);
  223. if (!tableStorage.Documents.Contains(Snapshot, lowerKey, writeBatch.Value))
  224. {
  225. logger.Debug("Document with key='{0}' was not found", key);
  226. return null;
  227. }
  228. var metadataDocument = ReadDocumentMetadata(key);
  229. if (metadataDocument == null)
  230. {
  231. logger.Warn(string.Format("Metadata of document with key='{0} was not found, but the document itself exists.", key));
  232. return null;
  233. }
  234. var documentData = ReadDocumentData(key, metadataDocument.Etag, metadataDocument.Metadata);
  235. logger.Debug("DocumentByKey() by key ='{0}'", key);
  236. var docSize = tableStorage.Documents.GetDataSize(Snapshot, lowerKey);
  237. var metadataSize = metadataIndex.GetDataSize(Snapshot, lowerKey);
  238. return new JsonDocument
  239. {
  240. DataAsJson = documentData,
  241. Etag = metadataDocument.Etag,
  242. Key = metadataDocument.Key, //original key - with user specified casing, etc.
  243. Metadata = metadataDocument.Metadata,
  244. SerializedSizeOnDisk = docSize + metadataSize,
  245. LastModified = metadataDocument.LastModified
  246. };
  247. }
  248. public JsonDocumentMetadata DocumentMetadataByKey(string key, TransactionInformation transactionInformation)
  249. {
  250. if (string.IsNullOrEmpty(key))
  251. throw new ArgumentNullException("key");
  252. var lowerKey = CreateKey(key);
  253. if (tableStorage.Documents.Contains(Snapshot, lowerKey, writeBatch.Value))
  254. return ReadDocumentMetadata(key);
  255. logger.Debug("Document with key='{0}' was not found", key);
  256. return null;
  257. }
  258. public bool DeleteDocument(string key, Etag etag, out RavenJObject metadata, out Etag deletedETag)
  259. {
  260. if (string.IsNullOrEmpty(key))
  261. throw new ArgumentNullException("key");
  262. var loweredKey = CreateKey(key);
  263. if(etag != null)
  264. EnsureDocumentEtagMatch(loweredKey, etag, "DELETE");
  265. ushort? existingVersion;
  266. if (!tableStorage.Documents.Contains(Snapshot, loweredKey, writeBatch.Value, out existingVersion))
  267. {
  268. logger.Debug("Document with key '{0}' was not found, and considered deleted", key);
  269. metadata = null;
  270. deletedETag = null;
  271. return false;
  272. }
  273. if (!metadataIndex.Contains(Snapshot, loweredKey, writeBatch.Value)) //data exists, but metadata is not --> precaution, should never be true
  274. {
  275. var errorString = string.Format("Document with key '{0}' was found, but its metadata wasn't found --> possible data corruption", key);
  276. throw new InvalidDataException(errorString);
  277. }
  278. var existingEtag = EnsureDocumentEtagMatch(key, etag, "DELETE");
  279. var documentMetadata = ReadDocumentMetadata(key);
  280. metadata = documentMetadata.Metadata;
  281. deletedETag = etag != null ? existingEtag : documentMetadata.Etag;
  282. tableStorage.Documents.Delete(writeBatch.Value, loweredKey, existingVersion);
  283. metadataIndex.Delete(writeBatch.Value, loweredKey);
  284. tableStorage.Documents.GetIndex(Tables.Documents.Indices.KeyByEtag)
  285. .Delete(writeBatch.Value, deletedETag);
  286. documentCacher.RemoveCachedDocument(loweredKey, etag);
  287. logger.Debug("Deleted document with key = '{0}'", key);
  288. return true;
  289. }
  290. public AddDocumentResult AddDocument(string key, Etag etag, RavenJObject data, RavenJObject metadata)
  291. {
  292. if (string.IsNullOrEmpty(key))
  293. throw new ArgumentNullException("key");
  294. if (key != null && Encoding.UTF8.GetByteCount(key) >= UInt16.MaxValue)
  295. throw new ArgumentException(string.Format("The dataKey must be a maximum of {0} bytes in Unicode, key is: '{1}'", UInt16.MaxValue, key), "key");
  296. Etag existingEtag;
  297. Etag newEtag;
  298. DateTime savedAt;
  299. var isUpdate = WriteDocumentData(key, etag, data, metadata, out newEtag, out existingEtag, out savedAt);
  300. logger.Debug("AddDocument() - {0} document with key = '{1}'", isUpdate ? "Updated" : "Added", key);
  301. return new AddDocumentResult
  302. {
  303. Etag = newEtag,
  304. PrevEtag = existingEtag,
  305. SavedAt = savedAt,
  306. Updated = isUpdate
  307. };
  308. }
  309. private bool PutDocumentMetadataInternal(string key, RavenJObject metadata, Etag newEtag, DateTime savedAt)
  310. {
  311. return WriteDocumentMetadata(new JsonDocumentMetadata
  312. {
  313. Key = key,
  314. Etag = newEtag,
  315. Metadata = metadata,
  316. LastModified = savedAt
  317. });
  318. }
  319. public void IncrementDocumentCount(int value)
  320. {
  321. //nothing to do here
  322. }
  323. public AddDocumentResult InsertDocument(string key, RavenJObject data, RavenJObject metadata, bool overwriteExisting)
  324. {
  325. if (string.IsNullOrEmpty(key))
  326. throw new ArgumentNullException("key");
  327. if (!overwriteExisting && tableStorage.Documents.Contains(Snapshot, CreateKey(key), writeBatch.Value))
  328. {
  329. throw new ConcurrencyException(string.Format("InsertDocument() - overwriteExisting is false and document with key = '{0}' already exists", key));
  330. }
  331. return AddDocument(key, null, data, metadata);
  332. }
  333. public void TouchDocument(string key, out Etag preTouchEtag, out Etag afterTouchEtag)
  334. {
  335. if (string.IsNullOrEmpty(key))
  336. throw new ArgumentNullException("key");
  337. var lowerKey = CreateKey(key);
  338. if (!tableStorage.Documents.Contains(Snapshot, lowerKey, writeBatch.Value))
  339. {
  340. logger.Debug("Document with dataKey='{0}' was not found", key);
  341. preTouchEtag = null;
  342. afterTouchEtag = null;
  343. return;
  344. }
  345. var metadata = ReadDocumentMetadata(key);
  346. var newEtag = uuidGenerator.CreateSequentialUuid(UuidType.Documents);
  347. afterTouchEtag = newEtag;
  348. preTouchEtag = metadata.Etag;
  349. metadata.Etag = newEtag;
  350. WriteDocumentMetadata(metadata,shouldIgnoreConcurrencyExceptions:true);
  351. var keyByEtagIndex = tableStorage.Documents.GetIndex(Tables.Documents.Indices.KeyByEtag);
  352. keyByEtagIndex.Delete(writeBatch.Value, preTouchEtag);
  353. keyByEtagIndex.Add(writeBatch.Value, newEtag, lowerKey);
  354. etagTouches.Add(preTouchEtag, afterTouchEtag);
  355. logger.Debug("TouchDocument() - document with key = '{0}'", key);
  356. }
  357. public Etag GetBestNextDocumentEtag(Etag etag)
  358. {
  359. if (etag == null) throw new ArgumentNullException("etag");
  360. using (var iter = tableStorage.Documents.GetIndex(Tables.Documents.Indices.KeyByEtag)
  361. .Iterate(Snapshot, writeBatch.Value))
  362. {
  363. if (!iter.Seek(etag.ToString()) &&
  364. !iter.Seek(Slice.BeforeAllKeys)) //if parameter etag not found, scan from beginning. if empty --> return original etag
  365. return etag;
  366. do
  367. {
  368. var docEtag = Etag.Parse(iter.CurrentKey.ToString());
  369. if (EtagUtil.IsGreaterThan(docEtag, etag))
  370. return docEtag;
  371. } while (iter.MoveNext());
  372. }
  373. return etag; //if not found, return the original etag
  374. }
  375. private Etag EnsureDocumentEtagMatch(string key, Etag etag, string method)
  376. {
  377. var metadata = ReadDocumentMetadata(key);
  378. if (metadata == null)
  379. return Etag.InvalidEtag;
  380. var existingEtag = metadata.Etag;
  381. if (etag != null)
  382. {
  383. Etag next;
  384. while (etagTouches.TryGetValue(etag, out next))
  385. {
  386. etag = next;
  387. }
  388. if (existingEtag != etag)
  389. {
  390. if (etag == Etag.Empty)
  391. {
  392. if (metadata.Metadata.ContainsKey(Constants.RavenDeleteMarker) &&
  393. metadata.Metadata.Value<bool>(Constants.RavenDeleteMarker))
  394. {
  395. return existingEtag;
  396. }
  397. }
  398. throw new ConcurrencyException(method + " attempted on document '" + key +
  399. "' using a non current etag")
  400. {
  401. ActualETag = existingEtag,
  402. ExpectedETag = etag
  403. };
  404. }
  405. }
  406. return existingEtag;
  407. }
  408. //returns true if it was update operation
  409. private bool WriteDocumentMetadata(JsonDocumentMetadata metadata,bool shouldIgnoreConcurrencyExceptions = false)
  410. {
  411. var metadataStream = CreateStream();
  412. metadataStream.Write(metadata.Etag);
  413. metadataStream.Write(metadata.Key);
  414. if (metadata.LastModified.HasValue)
  415. metadataStream.Write(metadata.LastModified.Value.ToBinary());
  416. else
  417. metadataStream.Write((long)0);
  418. metadata.Metadata.WriteTo(metadataStream);
  419. metadataStream.Position = 0;
  420. var loweredKey = CreateKey(metadata.Key);
  421. ushort? existingVersion;
  422. var isUpdate = metadataIndex.Contains(Snapshot, loweredKey, writeBatch.Value, out existingVersion);
  423. metadataIndex.Add(writeBatch.Value, loweredKey, metadataStream, existingVersion, shouldIgnoreConcurrencyExceptions);
  424. return isUpdate;
  425. }
  426. private JsonDocumentMetadata ReadDocumentMetadata(string key)
  427. {
  428. var loweredKey = CreateKey(key);
  429. var metadataReadResult = metadataIndex.Read(Snapshot, loweredKey, writeBatch.Value);
  430. if (metadataReadResult == null)
  431. return null;
  432. using (var stream = metadataReadResult.Reader.AsStream())
  433. {
  434. stream.Position = 0;
  435. var etag = stream.ReadEtag();
  436. var originalKey = stream.ReadString();
  437. var lastModifiedDateTimeBinary = stream.ReadInt64();
  438. var existingCachedDocument = documentCacher.GetCachedDocument(loweredKey, etag);
  439. var metadata = existingCachedDocument != null ? existingCachedDocument.Metadata : stream.ToJObject();
  440. var lastModified = DateTime.FromBinary(lastModifiedDateTimeBinary);
  441. return new JsonDocumentMetadata
  442. {
  443. Key = originalKey,
  444. Etag = etag,
  445. Metadata = metadata,
  446. LastModified = lastModified
  447. };
  448. }
  449. }
  450. private bool WriteDocumentData(string key, Etag etag, RavenJObject data, RavenJObject metadata, out Etag newEtag, out Etag existingEtag, out DateTime savedAt)
  451. {
  452. var keyByEtagDocumentIndex = tableStorage.Documents.GetIndex(Tables.Documents.Indices.KeyByEtag);
  453. var loweredKey = CreateKey(key);
  454. ushort? existingVersion;
  455. var isUpdate = tableStorage.Documents.Contains(Snapshot, loweredKey, writeBatch.Value, out existingVersion);
  456. existingEtag = null;
  457. if (isUpdate)
  458. {
  459. existingEtag = EnsureDocumentEtagMatch(loweredKey, etag, "PUT");
  460. keyByEtagDocumentIndex.Delete(writeBatch.Value, existingEtag);
  461. }
  462. else if (etag != null && etag != Etag.Empty)
  463. {
  464. throw new ConcurrencyException("PUT attempted on document '" + key +
  465. "' using a non current etag (document deleted)")
  466. {
  467. ExpectedETag = etag
  468. };
  469. }
  470. var dataStream = CreateStream();
  471. using (var finalDataStream = documentCodecs.Aggregate((Stream) new UndisposableStream(dataStream),
  472. (current, codec) => codec.Encode(loweredKey, data, metadata, current)))
  473. {
  474. data.WriteTo(finalDataStream);
  475. finalDataStream.Flush();
  476. }
  477. dataStream.Position = 0;
  478. tableStorage.Documents.Add(writeBatch.Value, loweredKey, dataStream, existingVersion ?? 0);
  479. newEtag = uuidGenerator.CreateSequentialUuid(UuidType.Documents);
  480. savedAt = SystemTime.UtcNow;
  481. var isUpdated = PutDocumentMetadataInternal(key, metadata, newEtag, savedAt);
  482. keyByEtagDocumentIndex.Add(writeBatch.Value, newEtag, loweredKey);
  483. return isUpdated;
  484. }
  485. private RavenJObject ReadDocumentData(string key, Etag existingEtag, RavenJObject metadata)
  486. {
  487. var loweredKey = CreateKey(key);
  488. var existingCachedDocument = documentCacher.GetCachedDocument(loweredKey, existingEtag);
  489. if (existingCachedDocument != null)
  490. return existingCachedDocument.Document;
  491. var documentReadResult = tableStorage.Documents.Read(Snapshot, loweredKey, writeBatch.Value);
  492. if (documentReadResult == null) //non existing document
  493. return null;
  494. using (var stream = documentReadResult.Reader.AsStream())
  495. {
  496. using (var decodedDocumentStream = documentCodecs.Aggregate(stream,
  497. (current, codec) => codec.Value.Decode(loweredKey, metadata, current)))
  498. {
  499. var documentData = decodedDocumentStream.ToJObject();
  500. documentCacher.SetCachedDocument(loweredKey, existingEtag, documentData, metadata, (int)stream.Length);
  501. return documentData;
  502. }
  503. }
  504. }
  505. public DebugDocumentStats GetDocumentStatsVerySlowly()
  506. {
  507. var sp = Stopwatch.StartNew();
  508. var stat = new DebugDocumentStats { Total = GetDocumentsCount() };
  509. var documentsByEtag = tableStorage.Documents.GetIndex(Tables.Documents.Indices.KeyByEtag);
  510. using (var iterator = documentsByEtag.Iterate(Snapshot, writeBatch.Value))
  511. {
  512. if (!iterator.Seek(Slice.BeforeAllKeys))
  513. {
  514. stat.TimeToGenerate = sp.Elapsed;
  515. return stat;
  516. }
  517. do
  518. {
  519. var key = GetKeyFromCurrent(iterator);
  520. var doc = DocumentByKey(key, null);
  521. var size = doc.SerializedSizeOnDisk;
  522. stat.TotalSize += size;
  523. if (key.StartsWith("Raven/", StringComparison.OrdinalIgnoreCase))
  524. {
  525. stat.System++;
  526. stat.SystemSize += size;
  527. }
  528. var metadata = ReadDocumentMetadata(key);
  529. var entityName = metadata.Metadata.Value<string>(Constants.RavenEntityName);
  530. if (string.IsNullOrEmpty(entityName))
  531. {
  532. stat.NoCollection++;
  533. stat.NoCollectionSize += size;
  534. }
  535. else
  536. {
  537. stat.IncrementCollection(entityName, size);
  538. }
  539. if (metadata.Metadata.ContainsKey(Constants.RavenDeleteMarker))
  540. stat.Tombstones++;
  541. }
  542. while (iterator.MoveNext());
  543. var sortedStat = stat.Collections.OrderByDescending(x => x.Value.Size).ToDictionary(x => x.Key, x => x.Value);
  544. stat.TimeToGenerate = sp.Elapsed;
  545. stat.Collections = sortedStat;
  546. return stat;
  547. }
  548. }
  549. }
  550. }