PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Raven.Database/DocumentDatabase.cs

https://github.com/kairogyn/ravendb
C# | 2616 lines | 2188 code | 354 blank | 74 comment | 332 complexity | fb0a12cc02853c01587ac98fc1e9bd9f MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, BSD-3-Clause, CC-BY-SA-3.0

Large files files are truncated, but you can click here to view the full file

  1. //-----------------------------------------------------------------------
  2. // <copyright file="DocumentDatabase.cs" company="Hibernating Rhinos LTD">
  3. // Copyright (c) Hibernating Rhinos LTD. All rights reserved.
  4. // </copyright>
  5. //-----------------------------------------------------------------------
  6. using System;
  7. using System.Collections.Concurrent;
  8. using System.Collections.Generic;
  9. using System.ComponentModel.Composition;
  10. using System.Diagnostics;
  11. using System.Globalization;
  12. using System.IO;
  13. using System.Linq;
  14. using System.Runtime.CompilerServices;
  15. using System.Security.Cryptography;
  16. using System.Text;
  17. using System.Threading;
  18. using System.Threading.Tasks;
  19. using Lucene.Net.Search;
  20. using Raven.Abstractions.Logging;
  21. using Raven.Abstractions.Util;
  22. using Raven.Database.Commercial;
  23. using Raven.Database.Impl.DTC;
  24. using Raven.Database.Prefetching;
  25. using Raven.Database.Queries;
  26. using Raven.Database.Server;
  27. using Raven.Database.Server.Connections;
  28. using Raven.Database.Server.Responders.Debugging;
  29. using Raven.Database.Util;
  30. using Raven.Abstractions;
  31. using Raven.Abstractions.Commands;
  32. using Raven.Abstractions.Data;
  33. using Raven.Abstractions.Exceptions;
  34. using Raven.Abstractions.Extensions;
  35. using Raven.Abstractions.Indexing;
  36. using Raven.Abstractions.Linq;
  37. using Raven.Abstractions.MEF;
  38. using Raven.Database.Config;
  39. using Raven.Database.Data;
  40. using Raven.Database.Extensions;
  41. using Raven.Database.Impl;
  42. using Raven.Database.Indexing;
  43. using Raven.Database.Json;
  44. using Raven.Database.Linq;
  45. using Raven.Database.Plugins;
  46. using Raven.Database.Storage;
  47. using Raven.Database.Tasks;
  48. using Constants = Raven.Abstractions.Data.Constants;
  49. using Raven.Json.Linq;
  50. using BitConverter = System.BitConverter;
  51. using Index = Raven.Database.Indexing.Index;
  52. using Task = System.Threading.Tasks.Task;
  53. using TransactionInformation = Raven.Abstractions.Data.TransactionInformation;
  54. namespace Raven.Database
  55. {
  56. public class DocumentDatabase : IDisposable
  57. {
  58. private readonly InMemoryRavenConfiguration configuration;
  59. [ImportMany]
  60. public OrderedPartCollection<AbstractRequestResponder> RequestResponders { get; set; }
  61. [ImportMany]
  62. public OrderedPartCollection<IStartupTask> StartupTasks { get; set; }
  63. [ImportMany]
  64. public OrderedPartCollection<AbstractAttachmentPutTrigger> AttachmentPutTriggers { get; set; }
  65. public InFlightTransactionalState InFlightTransactionalState
  66. {
  67. get { return inFlightTransactionalState; }
  68. }
  69. [ImportMany]
  70. public OrderedPartCollection<AbstractIndexQueryTrigger> IndexQueryTriggers { get; set; }
  71. [ImportMany]
  72. public OrderedPartCollection<AbstractAttachmentDeleteTrigger> AttachmentDeleteTriggers { get; set; }
  73. [ImportMany]
  74. public OrderedPartCollection<AbstractAttachmentReadTrigger> AttachmentReadTriggers { get; set; }
  75. [ImportMany]
  76. public OrderedPartCollection<AbstractPutTrigger> PutTriggers { get; set; }
  77. [ImportMany]
  78. public OrderedPartCollection<AbstractDeleteTrigger> DeleteTriggers { get; set; }
  79. [ImportMany]
  80. public OrderedPartCollection<AbstractIndexUpdateTrigger> IndexUpdateTriggers { get; set; }
  81. [ImportMany]
  82. public OrderedPartCollection<AbstractReadTrigger> ReadTriggers { get; set; }
  83. [ImportMany]
  84. public OrderedPartCollection<AbstractDynamicCompilationExtension> Extensions { get; set; }
  85. [ImportMany]
  86. public OrderedPartCollection<AbstractIndexCodec> IndexCodecs { get; set; }
  87. [ImportMany]
  88. public OrderedPartCollection<AbstractDocumentCodec> DocumentCodecs { get; set; }
  89. [ImportMany]
  90. public OrderedPartCollection<AbstractIndexReaderWarmer> IndexReaderWarmers { get; set; }
  91. private readonly List<IDisposable> toDispose = new List<IDisposable>();
  92. private long pendingTaskCounter;
  93. private readonly ConcurrentDictionary<long, PendingTaskAndState> pendingTasks = new ConcurrentDictionary<long, PendingTaskAndState>();
  94. private readonly InFlightTransactionalState inFlightTransactionalState;
  95. private class PendingTaskAndState
  96. {
  97. public Task Task;
  98. public object State;
  99. }
  100. /// <summary>
  101. /// The name of the database.
  102. /// Defaults to null for the root database (or embedded database), or the name of the database if this db is a tenant database
  103. /// </summary>
  104. public string Name { get; private set; }
  105. private readonly WorkContext workContext;
  106. private readonly IndexingExecuter indexingExecuter;
  107. public IndexingExecuter IndexingExecuter
  108. {
  109. get { return indexingExecuter; }
  110. }
  111. private readonly Prefetcher prefetcher;
  112. public Prefetcher Prefetcher
  113. {
  114. get { return prefetcher; }
  115. }
  116. /// <summary>
  117. /// Requires to avoid having serialize writes to the same attachments
  118. /// </summary>
  119. private readonly ConcurrentDictionary<string, object> putAttachmentSerialLock = new ConcurrentDictionary<string, object>(StringComparer.OrdinalIgnoreCase);
  120. internal PutSerialLock DocumentLock { get; private set; }
  121. /// <summary>
  122. /// This is used to hold state associated with this instance by external extensions
  123. /// </summary>
  124. public AtomicDictionary<object> ExtensionsState { get; private set; }
  125. public TaskScheduler BackgroundTaskScheduler { get { return backgroundTaskScheduler; } }
  126. private readonly ThreadLocal<bool> disableAllTriggers = new ThreadLocal<bool>(() => false);
  127. private System.Threading.Tasks.Task indexingBackgroundTask;
  128. private System.Threading.Tasks.Task reducingBackgroundTask;
  129. private readonly TaskScheduler backgroundTaskScheduler;
  130. private readonly object idleLocker = new object();
  131. private static readonly ILog log = LogManager.GetCurrentClassLogger();
  132. private readonly SizeLimitedConcurrentDictionary<string, TouchedDocumentInfo> recentTouches;
  133. public DocumentDatabase(InMemoryRavenConfiguration configuration, TransportState transportState = null)
  134. {
  135. DocumentLock = new PutSerialLock();
  136. this.configuration = configuration;
  137. this.transportState = transportState ?? new TransportState();
  138. using (LogManager.OpenMappedContext("database", configuration.DatabaseName ?? Constants.SystemDatabase))
  139. {
  140. log.Debug("Start loading the following database: {0}", configuration.DatabaseName ?? Constants.SystemDatabase);
  141. if (configuration.IsTenantDatabase == false)
  142. {
  143. validateLicense = new ValidateLicense();
  144. validateLicense.Execute(configuration);
  145. }
  146. AppDomain.CurrentDomain.DomainUnload += DomainUnloadOrProcessExit;
  147. AppDomain.CurrentDomain.ProcessExit += DomainUnloadOrProcessExit;
  148. Name = configuration.DatabaseName;
  149. backgroundTaskScheduler = configuration.CustomTaskScheduler ?? TaskScheduler.Default;
  150. ExtensionsState = new AtomicDictionary<object>();
  151. Configuration = configuration;
  152. ExecuteAlterConfiguration();
  153. recentTouches = new SizeLimitedConcurrentDictionary<string, TouchedDocumentInfo>(configuration.MaxRecentTouchesToRemember, StringComparer.OrdinalIgnoreCase);
  154. configuration.Container.SatisfyImportsOnce(this);
  155. workContext = new WorkContext
  156. {
  157. Database = this,
  158. DatabaseName = Name,
  159. IndexUpdateTriggers = IndexUpdateTriggers,
  160. ReadTriggers = ReadTriggers,
  161. RaiseIndexChangeNotification = RaiseNotifications,
  162. TaskScheduler = backgroundTaskScheduler,
  163. Configuration = configuration,
  164. IndexReaderWarmers = IndexReaderWarmers
  165. };
  166. TransactionalStorage = configuration.CreateTransactionalStorage(workContext.HandleWorkNotifications);
  167. try
  168. {
  169. sequentialUuidGenerator = new SequentialUuidGenerator();
  170. TransactionalStorage.Initialize(sequentialUuidGenerator, DocumentCodecs);
  171. }
  172. catch (Exception)
  173. {
  174. TransactionalStorage.Dispose();
  175. throw;
  176. }
  177. try
  178. {
  179. inFlightTransactionalState = TransactionalStorage.GetInFlightTransactionalState(Put, Delete);
  180. TransactionalStorage.Batch(actions =>
  181. sequentialUuidGenerator.EtagBase = actions.General.GetNextIdentityValue("Raven/Etag"));
  182. // Index codecs must be initialized before we try to read an index
  183. InitializeIndexCodecTriggers();
  184. IndexDefinitionStorage = new IndexDefinitionStorage(
  185. configuration,
  186. TransactionalStorage,
  187. configuration.DataDirectory,
  188. configuration.Container.GetExportedValues<AbstractViewGenerator>(),
  189. Extensions);
  190. IndexStorage = new IndexStorage(IndexDefinitionStorage, configuration, this);
  191. CompleteWorkContextSetup();
  192. prefetcher = new Prefetcher(workContext);
  193. indexingExecuter = new IndexingExecuter(workContext, prefetcher);
  194. InitializeTriggersExceptIndexCodecs();
  195. SecondStageInitialization();
  196. ExecuteStartupTasks();
  197. log.Debug("Finish loading the following database: {0}", configuration.DatabaseName ?? Constants.SystemDatabase);
  198. }
  199. catch (Exception)
  200. {
  201. Dispose();
  202. throw;
  203. }
  204. }
  205. }
  206. private void SecondStageInitialization()
  207. {
  208. DocumentCodecs.OfType<IRequiresDocumentDatabaseInitialization>()
  209. .Concat(PutTriggers.OfType<IRequiresDocumentDatabaseInitialization>())
  210. .Concat(DeleteTriggers.OfType<IRequiresDocumentDatabaseInitialization>())
  211. .Concat(IndexCodecs.OfType<IRequiresDocumentDatabaseInitialization>())
  212. .Concat(IndexQueryTriggers.OfType<IRequiresDocumentDatabaseInitialization>())
  213. .Concat(AttachmentPutTriggers.OfType<IRequiresDocumentDatabaseInitialization>())
  214. .Concat(AttachmentDeleteTriggers.OfType<IRequiresDocumentDatabaseInitialization>())
  215. .Concat(AttachmentReadTriggers.OfType<IRequiresDocumentDatabaseInitialization>())
  216. .Concat(IndexUpdateTriggers.OfType<IRequiresDocumentDatabaseInitialization>())
  217. .Apply(initialization => initialization.SecondStageInit());
  218. }
  219. private void CompleteWorkContextSetup()
  220. {
  221. workContext.IndexStorage = IndexStorage;
  222. workContext.TransactionalStorage = TransactionalStorage;
  223. workContext.IndexDefinitionStorage = IndexDefinitionStorage;
  224. workContext.Init(Name);
  225. }
  226. private void DomainUnloadOrProcessExit(object sender, EventArgs eventArgs)
  227. {
  228. Dispose();
  229. }
  230. private void InitializeTriggersExceptIndexCodecs()
  231. {
  232. DocumentCodecs
  233. //.Init(disableAllTriggers) // Document codecs should always be activated (RavenDB-576)
  234. .OfType<IRequiresDocumentDatabaseInitialization>().Apply(initialization => initialization.Initialize(this));
  235. PutTriggers
  236. .Init(disableAllTriggers)
  237. .OfType<IRequiresDocumentDatabaseInitialization>().Apply(initialization => initialization.Initialize(this));
  238. DeleteTriggers
  239. .Init(disableAllTriggers)
  240. .OfType<IRequiresDocumentDatabaseInitialization>().Apply(initialization => initialization.Initialize(this));
  241. ReadTriggers
  242. .Init(disableAllTriggers)
  243. .OfType<IRequiresDocumentDatabaseInitialization>().Apply(initialization => initialization.Initialize(this));
  244. IndexQueryTriggers
  245. .Init(disableAllTriggers)
  246. .OfType<IRequiresDocumentDatabaseInitialization>().Apply(initialization => initialization.Initialize(this));
  247. AttachmentPutTriggers
  248. .Init(disableAllTriggers)
  249. .OfType<IRequiresDocumentDatabaseInitialization>().Apply(initialization => initialization.Initialize(this));
  250. AttachmentDeleteTriggers
  251. .Init(disableAllTriggers)
  252. .OfType<IRequiresDocumentDatabaseInitialization>().Apply(initialization => initialization.Initialize(this));
  253. AttachmentReadTriggers
  254. .Init(disableAllTriggers)
  255. .OfType<IRequiresDocumentDatabaseInitialization>().Apply(initialization => initialization.Initialize(this));
  256. IndexUpdateTriggers
  257. .Init(disableAllTriggers)
  258. .OfType<IRequiresDocumentDatabaseInitialization>().Apply(initialization => initialization.Initialize(this));
  259. }
  260. private void InitializeIndexCodecTriggers()
  261. {
  262. IndexCodecs
  263. .Init(disableAllTriggers)
  264. .OfType<IRequiresDocumentDatabaseInitialization>().Apply(initialization => initialization.Initialize(this));
  265. }
  266. private void ExecuteAlterConfiguration()
  267. {
  268. foreach (var alterConfiguration in Configuration.Container.GetExportedValues<IAlterConfiguration>())
  269. {
  270. alterConfiguration.AlterConfiguration(Configuration);
  271. }
  272. }
  273. private void ExecuteStartupTasks()
  274. {
  275. using (LogContext.WithDatabase(Name))
  276. {
  277. foreach (var task in StartupTasks)
  278. {
  279. var disposable = task.Value as IDisposable;
  280. if (disposable != null)
  281. toDispose.Add(disposable);
  282. task.Value.Execute(this);
  283. }
  284. }
  285. }
  286. public DatabaseStatistics Statistics
  287. {
  288. get
  289. {
  290. var result = new DatabaseStatistics
  291. {
  292. CurrentNumberOfItemsToIndexInSingleBatch = workContext.CurrentNumberOfItemsToIndexInSingleBatch,
  293. CurrentNumberOfItemsToReduceInSingleBatch = workContext.CurrentNumberOfItemsToReduceInSingleBatch,
  294. IndexingBatchInfo = workContext.LastActualIndexingBatchInfo.ToArray(),
  295. InMemoryIndexingQueueSize = prefetcher.GetInMemoryIndexingQueueSize(PrefetchingUser.Indexer),
  296. Prefetches = workContext.FutureBatchStats.OrderBy(x => x.Timestamp).ToArray(),
  297. CountOfIndexes = IndexStorage.Indexes.Length,
  298. DatabaseTransactionVersionSizeInMB = ConvertBytesToMBs(workContext.TransactionalStorage.GetDatabaseTransactionVersionSizeInBytes()),
  299. Errors = workContext.Errors,
  300. DatabaseId = TransactionalStorage.Id,
  301. Triggers = PutTriggers.Select(x => new DatabaseStatistics.TriggerInfo { Name = x.ToString(), Type = "Put" })
  302. .Concat(DeleteTriggers.Select(x => new DatabaseStatistics.TriggerInfo { Name = x.ToString(), Type = "Delete" }))
  303. .Concat(ReadTriggers.Select(x => new DatabaseStatistics.TriggerInfo { Name = x.ToString(), Type = "Read" }))
  304. .Concat(IndexUpdateTriggers.Select(x => new DatabaseStatistics.TriggerInfo { Name = x.ToString(), Type = "Index Update" }))
  305. .ToArray(),
  306. Extensions = Configuration.ReportExtensions(
  307. typeof(IStartupTask),
  308. typeof(AbstractReadTrigger),
  309. typeof(AbstractDeleteTrigger),
  310. typeof(AbstractPutTrigger),
  311. typeof(AbstractDocumentCodec),
  312. typeof(AbstractIndexCodec),
  313. typeof(AbstractDynamicCompilationExtension),
  314. typeof(AbstractIndexQueryTrigger),
  315. typeof(AbstractIndexUpdateTrigger),
  316. typeof(AbstractAnalyzerGenerator),
  317. typeof(AbstractAttachmentDeleteTrigger),
  318. typeof(AbstractAttachmentPutTrigger),
  319. typeof(AbstractAttachmentReadTrigger),
  320. typeof(AbstractBackgroundTask),
  321. typeof(IAlterConfiguration)
  322. ),
  323. };
  324. TransactionalStorage.Batch(actions =>
  325. {
  326. result.LastDocEtag = actions.Staleness.GetMostRecentDocumentEtag();
  327. result.LastAttachmentEtag = actions.Staleness.GetMostRecentAttachmentEtag();
  328. result.ApproximateTaskCount = actions.Tasks.ApproximateTaskCount;
  329. result.CountOfDocuments = actions.Documents.GetDocumentsCount();
  330. result.StaleIndexes = IndexStorage.Indexes
  331. .Where(s =>
  332. {
  333. var indexInstance = IndexStorage.GetIndexInstance(s);
  334. return (indexInstance != null && indexInstance.IsMapIndexingInProgress) ||
  335. actions.Staleness.IsIndexStale(s, null, null);
  336. }).ToArray();
  337. result.Indexes = actions.Indexing.GetIndexesStats().Where(x => x != null).ToArray();
  338. });
  339. if (result.Indexes != null)
  340. {
  341. foreach (var index in result.Indexes)
  342. {
  343. try
  344. {
  345. index.LastQueryTimestamp = IndexStorage.GetLastQueryTime(index.Name);
  346. index.Performance = IndexStorage.GetIndexingPerformance(index.Name);
  347. index.IsOnRam = IndexStorage.IndexOnRam(index.Name);
  348. var indexDefinition = IndexDefinitionStorage.GetIndexDefinition(index.Name);
  349. if (indexDefinition != null)
  350. index.LockMode = indexDefinition.LockMode;
  351. index.ForEntityName = IndexDefinitionStorage.GetViewGenerator(index.Name).ForEntityNames.ToList();
  352. IndexSearcher searcher;
  353. using (IndexStorage.GetCurrentIndexSearcher(index.Name, out searcher))
  354. {
  355. index.DocsCount = searcher.IndexReader.NumDocs();
  356. }
  357. }
  358. catch (Exception)
  359. {
  360. // might happen if the index was deleted mid operation
  361. // we don't really care for that, so we ignore this
  362. }
  363. }
  364. }
  365. return result;
  366. }
  367. }
  368. private decimal ConvertBytesToMBs(long bytes)
  369. {
  370. return Math.Round(bytes / 1024.0m / 1024.0m, 2);
  371. }
  372. public InMemoryRavenConfiguration Configuration
  373. {
  374. get;
  375. private set;
  376. }
  377. public ITransactionalStorage TransactionalStorage { get; private set; }
  378. public IndexDefinitionStorage IndexDefinitionStorage { get; private set; }
  379. public IndexStorage IndexStorage { get; private set; }
  380. public event EventHandler Disposing;
  381. public void Dispose()
  382. {
  383. if (disposed)
  384. return;
  385. log.Debug("Start shutdown the following database: {0}", Name ?? Constants.SystemDatabase);
  386. var onDisposing = Disposing;
  387. if (onDisposing != null)
  388. {
  389. try
  390. {
  391. onDisposing(this, EventArgs.Empty);
  392. }
  393. catch (Exception e)
  394. {
  395. log.WarnException("Error when notifying about db disposal, ignoring error and continuing with disposal", e);
  396. }
  397. }
  398. var exceptionAggregator = new ExceptionAggregator(log, "Could not properly dispose of DatabaseDocument");
  399. exceptionAggregator.Execute(() =>
  400. {
  401. AppDomain.CurrentDomain.DomainUnload -= DomainUnloadOrProcessExit;
  402. AppDomain.CurrentDomain.ProcessExit -= DomainUnloadOrProcessExit;
  403. disposed = true;
  404. if (workContext != null)
  405. workContext.StopWorkRude();
  406. });
  407. if (validateLicense != null)
  408. exceptionAggregator.Execute(validateLicense.Dispose);
  409. exceptionAggregator.Execute(() =>
  410. {
  411. if (ExtensionsState == null)
  412. return;
  413. foreach (var value in ExtensionsState.Values.OfType<IDisposable>())
  414. {
  415. exceptionAggregator.Execute(value.Dispose);
  416. }
  417. });
  418. exceptionAggregator.Execute(() =>
  419. {
  420. if (toDispose == null)
  421. return;
  422. foreach (var shouldDispose in toDispose)
  423. {
  424. exceptionAggregator.Execute(shouldDispose.Dispose);
  425. }
  426. });
  427. exceptionAggregator.Execute(() =>
  428. {
  429. foreach (var shouldDispose in pendingTasks)
  430. {
  431. var pendingTaskAndState = shouldDispose.Value;
  432. exceptionAggregator.Execute(() =>
  433. {
  434. try
  435. {
  436. pendingTaskAndState.Task.Wait();
  437. }
  438. catch (Exception)
  439. {
  440. // we explictly don't care about this during shutdown
  441. }
  442. });
  443. }
  444. pendingTasks.Clear();
  445. });
  446. exceptionAggregator.Execute(() =>
  447. {
  448. if (indexingBackgroundTask != null)
  449. indexingBackgroundTask.Wait();
  450. });
  451. exceptionAggregator.Execute(() =>
  452. {
  453. if (reducingBackgroundTask != null)
  454. reducingBackgroundTask.Wait();
  455. });
  456. exceptionAggregator.Execute(() =>
  457. {
  458. var disposable = backgroundTaskScheduler as IDisposable;
  459. if (disposable != null)
  460. disposable.Dispose();
  461. });
  462. if (TransactionalStorage != null)
  463. exceptionAggregator.Execute(TransactionalStorage.Dispose);
  464. if (IndexStorage != null)
  465. exceptionAggregator.Execute(IndexStorage.Dispose);
  466. if (Configuration != null)
  467. exceptionAggregator.Execute(Configuration.Dispose);
  468. exceptionAggregator.Execute(disableAllTriggers.Dispose);
  469. if (workContext != null)
  470. exceptionAggregator.Execute(workContext.Dispose);
  471. exceptionAggregator.ThrowIfNeeded();
  472. log.Debug("Finished shutdown the following database: {0}", Name ?? Constants.SystemDatabase);
  473. }
  474. public void StopBackgroundWorkers()
  475. {
  476. workContext.StopWork();
  477. if (indexingBackgroundTask != null)
  478. indexingBackgroundTask.Wait();
  479. if (reducingBackgroundTask != null)
  480. reducingBackgroundTask.Wait();
  481. backgroundWorkersSpun = false;
  482. }
  483. public void StopIndexingWorkers()
  484. {
  485. workContext.StopIndexing();
  486. try
  487. {
  488. indexingBackgroundTask.Wait();
  489. }
  490. catch (Exception e)
  491. {
  492. log.WarnException("Error while trying to stop background indexing", e);
  493. }
  494. try
  495. {
  496. reducingBackgroundTask.Wait();
  497. }
  498. catch (Exception e)
  499. {
  500. log.WarnException("Error while trying to stop background reducing", e);
  501. }
  502. backgroundWorkersSpun = false;
  503. }
  504. public WorkContext WorkContext
  505. {
  506. get { return workContext; }
  507. }
  508. private volatile bool backgroundWorkersSpun;
  509. public void SpinBackgroundWorkers()
  510. {
  511. if (backgroundWorkersSpun)
  512. throw new InvalidOperationException("The background workers has already been spun and cannot be spun again");
  513. backgroundWorkersSpun = true;
  514. workContext.StartWork();
  515. indexingBackgroundTask = Task.Factory.StartNew(
  516. indexingExecuter.Execute,
  517. CancellationToken.None, TaskCreationOptions.LongRunning, backgroundTaskScheduler);
  518. reducingBackgroundTask = Task.Factory.StartNew(
  519. new ReducingExecuter(workContext).Execute,
  520. CancellationToken.None, TaskCreationOptions.LongRunning, backgroundTaskScheduler);
  521. }
  522. public void SpinIndexingWorkers()
  523. {
  524. if (backgroundWorkersSpun)
  525. throw new InvalidOperationException("The background workers has already been spun and cannot be spun again");
  526. backgroundWorkersSpun = true;
  527. workContext.StartIndexing();
  528. indexingBackgroundTask = System.Threading.Tasks.Task.Factory.StartNew(
  529. indexingExecuter.Execute,
  530. CancellationToken.None, TaskCreationOptions.LongRunning, backgroundTaskScheduler);
  531. reducingBackgroundTask = System.Threading.Tasks.Task.Factory.StartNew(
  532. new ReducingExecuter(workContext).Execute,
  533. CancellationToken.None, TaskCreationOptions.LongRunning, backgroundTaskScheduler);
  534. }
  535. public void RaiseNotifications(DocumentChangeNotification obj, RavenJObject metadata)
  536. {
  537. TransportState.Send(obj);
  538. var onDocumentChange = OnDocumentChange;
  539. if (onDocumentChange != null)
  540. onDocumentChange(this, obj, metadata);
  541. }
  542. public void RaiseNotifications(IndexChangeNotification obj)
  543. {
  544. TransportState.Send(obj);
  545. }
  546. public void RaiseNotifications(ReplicationConflictNotification obj)
  547. {
  548. TransportState.Send(obj);
  549. }
  550. public void RaiseNotifications(BulkInsertChangeNotification obj)
  551. {
  552. TransportState.Send(obj);
  553. }
  554. public event Action<DocumentDatabase, DocumentChangeNotification, RavenJObject> OnDocumentChange;
  555. public void RunIdleOperations()
  556. {
  557. var tryEnter = Monitor.TryEnter(idleLocker);
  558. try
  559. {
  560. if (tryEnter == false)
  561. return;
  562. TransportState.OnIdle();
  563. IndexStorage.RunIdleOperations();
  564. ClearCompletedPendingTasks();
  565. }
  566. finally
  567. {
  568. if (tryEnter)
  569. Monitor.Exit(idleLocker);
  570. }
  571. }
  572. private void ClearCompletedPendingTasks()
  573. {
  574. foreach (var taskAndState in pendingTasks)
  575. {
  576. var task = taskAndState.Value.Task;
  577. if (task.IsCompleted || task.IsCanceled || task.IsFaulted)
  578. {
  579. PendingTaskAndState value;
  580. pendingTasks.TryRemove(taskAndState.Key, out value);
  581. }
  582. if (task.Exception != null)
  583. {
  584. log.InfoException("Failed to execute background task " + taskAndState.Key, task.Exception);
  585. }
  586. }
  587. }
  588. public JsonDocument Get(string key, TransactionInformation transactionInformation)
  589. {
  590. if (key == null)
  591. throw new ArgumentNullException("key");
  592. key = key.Trim();
  593. JsonDocument document = null;
  594. if (transactionInformation == null ||
  595. inFlightTransactionalState.TryGet(key, transactionInformation, out document) == false)
  596. {
  597. // first we check the dtc state, then the storage, to avoid race conditions
  598. var nonAuthoritativeInformationBehavior = inFlightTransactionalState.GetNonAuthoritativeInformationBehavior<JsonDocument>(transactionInformation, key);
  599. TransactionalStorage.Batch(actions => { document = actions.Documents.DocumentByKey(key, transactionInformation); });
  600. if (nonAuthoritativeInformationBehavior != null)
  601. document = nonAuthoritativeInformationBehavior(document);
  602. }
  603. DocumentRetriever.EnsureIdInMetadata(document);
  604. return new DocumentRetriever(null, ReadTriggers, inFlightTransactionalState)
  605. .ExecuteReadTriggers(document, transactionInformation, ReadOperation.Load);
  606. }
  607. public JsonDocumentMetadata GetDocumentMetadata(string key, TransactionInformation transactionInformation)
  608. {
  609. if (key == null)
  610. throw new ArgumentNullException("key");
  611. key = key.Trim();
  612. JsonDocumentMetadata document = null;
  613. if (transactionInformation == null ||
  614. inFlightTransactionalState.TryGet(key, transactionInformation, out document) == false)
  615. {
  616. var nonAuthoritativeInformationBehavior = inFlightTransactionalState.GetNonAuthoritativeInformationBehavior<JsonDocumentMetadata>(transactionInformation, key);
  617. TransactionalStorage.Batch(actions =>
  618. {
  619. document = actions.Documents.DocumentMetadataByKey(key, transactionInformation);
  620. });
  621. if (nonAuthoritativeInformationBehavior != null)
  622. document = nonAuthoritativeInformationBehavior(document);
  623. }
  624. DocumentRetriever.EnsureIdInMetadata(document);
  625. return new DocumentRetriever(null, ReadTriggers, inFlightTransactionalState)
  626. .ProcessReadVetoes(document, transactionInformation, ReadOperation.Load);
  627. }
  628. public PutResult Put(string key, Etag etag, RavenJObject document, RavenJObject metadata, TransactionInformation transactionInformation)
  629. {
  630. workContext.PerformanceCounters.DocsPerSecond.Increment();
  631. key = string.IsNullOrWhiteSpace(key) ? Guid.NewGuid().ToString() : key.Trim();
  632. RemoveReservedProperties(document);
  633. RemoveMetadataReservedProperties(metadata);
  634. Etag newEtag = Etag.Empty;
  635. using (DocumentLock.Lock())
  636. {
  637. TransactionalStorage.Batch(actions =>
  638. {
  639. if (key.EndsWith("/"))
  640. {
  641. key += GetNextIdentityValueWithoutOverwritingOnExistingDocuments(key, actions,
  642. transactionInformation);
  643. }
  644. AssertPutOperationNotVetoed(key, metadata, document, transactionInformation);
  645. if (transactionInformation == null)
  646. {
  647. if (inFlightTransactionalState.IsModified(key))
  648. throw new ConcurrencyException("PUT attempted on : " + key +
  649. " while it is being locked by another transaction");
  650. PutTriggers.Apply(trigger => trigger.OnPut(key, document, metadata, null));
  651. var addDocumentResult = actions.Documents.AddDocument(key, etag, document, metadata);
  652. newEtag = addDocumentResult.Etag;
  653. CheckReferenceBecauseOfDocumentUpdate(key, actions);
  654. metadata[Constants.LastModified] = addDocumentResult.SavedAt;
  655. metadata.EnsureSnapshot(
  656. "Metadata was written to the database, cannot modify the document after it was written (changes won't show up in the db). Did you forget to call CreateSnapshot() to get a clean copy?");
  657. document.EnsureSnapshot(
  658. "Document was written to the database, cannot modify the document after it was written (changes won't show up in the db). Did you forget to call CreateSnapshot() to get a clean copy?");
  659. actions.AfterStorageCommitBeforeWorkNotifications(new JsonDocument
  660. {
  661. Metadata = metadata,
  662. Key = key,
  663. DataAsJson = document,
  664. Etag = newEtag,
  665. LastModified = addDocumentResult.SavedAt,
  666. SkipDeleteFromIndex = addDocumentResult.Updated == false
  667. }, documents => prefetcher.AfterStorageCommitBeforeWorkNotifications(PrefetchingUser.Indexer, documents));
  668. if (addDocumentResult.Updated)
  669. prefetcher.AfterUpdate(key, addDocumentResult.PrevEtag);
  670. PutTriggers.Apply(trigger => trigger.AfterPut(key, document, metadata, newEtag, null));
  671. TransactionalStorage
  672. .ExecuteImmediatelyOrRegisterForSynchronization(() =>
  673. {
  674. PutTriggers.Apply(trigger => trigger.AfterCommit(key, document, metadata, newEtag));
  675. RaiseNotifications(new DocumentChangeNotification
  676. {
  677. Id = key,
  678. Type = DocumentChangeTypes.Put,
  679. Etag = newEtag,
  680. }, metadata);
  681. });
  682. workContext.ShouldNotifyAboutWork(() => "PUT " + key);
  683. }
  684. else
  685. {
  686. var doc = actions.Documents.DocumentMetadataByKey(key, null);
  687. newEtag = inFlightTransactionalState.AddDocumentInTransaction(key, etag, document, metadata,
  688. transactionInformation,
  689. doc == null
  690. ? Etag.Empty
  691. : doc.Etag,
  692. sequentialUuidGenerator);
  693. }
  694. });
  695. log.Debug("Put document {0} with etag {1}", key, newEtag);
  696. return new PutResult
  697. {
  698. Key = key,
  699. ETag = newEtag
  700. };
  701. }
  702. }
  703. internal void CheckReferenceBecauseOfDocumentUpdate(string key, IStorageActionsAccessor actions)
  704. {
  705. TouchedDocumentInfo touch;
  706. recentTouches.TryRemove(key, out touch);
  707. foreach (var referencing in actions.Indexing.GetDocumentsReferencing(key))
  708. {
  709. Etag preTouchEtag;
  710. Etag afterTouchEtag;
  711. try
  712. {
  713. actions.Documents.TouchDocument(referencing, out preTouchEtag, out afterTouchEtag);
  714. }
  715. catch (ConcurrencyException)
  716. {
  717. continue;
  718. }
  719. if (preTouchEtag == null || afterTouchEtag == null)
  720. continue;
  721. actions.General.MaybePulseTransaction();
  722. recentTouches.Set(referencing, new TouchedDocumentInfo
  723. {
  724. PreTouchEtag = preTouchEtag,
  725. TouchedEtag = afterTouchEtag
  726. });
  727. }
  728. }
  729. public long GetNextIdentityValueWithoutOverwritingOnExistingDocuments(string key,
  730. IStorageActionsAccessor actions,
  731. TransactionInformation transactionInformation)
  732. {
  733. int tries;
  734. return GetNextIdentityValueWithoutOverwritingOnExistingDocuments(key, actions, transactionInformation, out tries);
  735. }
  736. public long GetNextIdentityValueWithoutOverwritingOnExistingDocuments(string key,
  737. IStorageActionsAccessor actions,
  738. TransactionInformation transactionInformation,
  739. out int tries)
  740. {
  741. long nextIdentityValue = actions.General.GetNextIdentityValue(key);
  742. if (actions.Documents.DocumentMetadataByKey(key + nextIdentityValue, transactionInformation) == null)
  743. {
  744. tries = 1;
  745. return nextIdentityValue;
  746. }
  747. tries = 1;
  748. // there is already a document with this id, this means that we probably need to search
  749. // for an opening in potentially large data set.
  750. var lastKnownBusy = nextIdentityValue;
  751. var maybeFree = nextIdentityValue * 2;
  752. var lastKnownFree = long.MaxValue;
  753. while (true)
  754. {
  755. tries++;
  756. if (actions.Documents.DocumentMetadataByKey(key + maybeFree, transactionInformation) == null)
  757. {
  758. if (lastKnownBusy + 1 == maybeFree)
  759. {
  760. actions.General.SetIdentityValue(key, maybeFree);
  761. return maybeFree;
  762. }
  763. lastKnownFree = maybeFree;
  764. maybeFree = Math.Max(maybeFree - (maybeFree - lastKnownBusy) / 2, lastKnownBusy + 1);
  765. }
  766. else
  767. {
  768. lastKnownBusy = maybeFree;
  769. maybeFree = Math.Min(lastKnownFree, maybeFree * 2);
  770. }
  771. }
  772. }
  773. private void AssertPutOperationNotVetoed(string key, RavenJObject metadata, RavenJObject document, TransactionInformation transactionInformation)
  774. {
  775. var vetoResult = PutTriggers
  776. .Select(trigger => new { Trigger = trigger, VetoResult = trigger.AllowPut(key, document, metadata, transactionInformation) })
  777. .FirstOrDefault(x => x.VetoResult.IsAllowed == false);
  778. if (vetoResult != null)
  779. {
  780. throw new OperationVetoedException("PUT vetoed on document " + key + " by " + vetoResult.Trigger + " because: " + vetoResult.VetoResult.Reason);
  781. }
  782. }
  783. private void AssertAttachmentPutOperationNotVetoed(string key, RavenJObject metadata, Stream data)
  784. {
  785. var vetoResult = AttachmentPutTriggers
  786. .Select(trigger => new { Trigger = trigger, VetoResult = trigger.AllowPut(key, data, metadata) })
  787. .FirstOrDefault(x => x.VetoResult.IsAllowed == false);
  788. if (vetoResult != null)
  789. {
  790. throw new OperationVetoedException("PUT vetoed on attachment " + key + " by " + vetoResult.Trigger +
  791. " because: " + vetoResult.VetoResult.Reason);
  792. }
  793. }
  794. private void AssertAttachmentDeleteOperationNotVetoed(string key)
  795. {
  796. var vetoResult = AttachmentDeleteTriggers
  797. .Select(trigger => new { Trigger = trigger, VetoResult = trigger.AllowDelete(key) })
  798. .FirstOrDefault(x => x.VetoResult.IsAllowed == false);
  799. if (vetoResult != null)
  800. {
  801. throw new OperationVetoedException("DELETE vetoed on attachment " + key + " by " + vetoResult.Trigger +
  802. " because: " + vetoResult.VetoResult.Reason);
  803. }
  804. }
  805. private void AssertDeleteOperationNotVetoed(string key, TransactionInformation transactionInformation)
  806. {
  807. var vetoResult = DeleteTriggers
  808. .Select(trigger => new { Trigger = trigger, VetoResult = trigger.AllowDelete(key, transactionInformation) })
  809. .FirstOrDefault(x => x.VetoResult.IsAllowed == false);
  810. if (vetoResult != null)
  811. {
  812. throw new OperationVetoedException("DELETE vetoed on document " + key + " by " + vetoResult.Trigger +
  813. " because: " + vetoResult.VetoResult.Reason);
  814. }
  815. }
  816. private static void RemoveMetadataReservedProperties(RavenJObject metadata)
  817. {
  818. RemoveReservedProperties(metadata);
  819. metadata.Remove("Raven-Last-Modified");
  820. metadata.Remove("Last-Modified");
  821. }
  822. private static void RemoveReservedProperties(RavenJObject document)
  823. {
  824. document.Remove(string.Empty);
  825. var toRemove = document.Keys.Where(propertyName => propertyName.StartsWith("@") || headersToIgnoreServer.Contains(propertyName)).ToList();
  826. foreach (var propertyName in toRemove)
  827. {
  828. document.Remove(propertyName);
  829. }
  830. }
  831. private static readonly HashSet<string> headersToIgnoreServer = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
  832. {
  833. Constants.RavenLastModified,
  834. };
  835. public bool Delete(string key, Etag etag, TransactionInformation transactionInformation)
  836. {
  837. RavenJObject metadata;
  838. return Delete(key, etag, transactionInformation, out metadata);
  839. }
  840. public bool Delete(string key, Etag etag, TransactionInformation transactionInformation, out RavenJObject metadata)
  841. {
  842. if (key == null)
  843. throw new ArgumentNullException("key");
  844. key = key.Trim();
  845. var deleted = false;
  846. log.Debug("Delete a document with key: {0} and etag {1}", key, etag);
  847. RavenJObject metadataVar = null;
  848. using (DocumentLock.Lock())
  849. {
  850. TransactionalStorage.Batch(actions =>
  851. {
  852. AssertDeleteOperationNotVetoed(key, transactionInformation);
  853. if (transactionInformation == null)
  854. {
  855. DeleteTriggers.Apply(trigger => trigger.OnDelete(key, null));
  856. Etag deletedETag;
  857. if (actions.Documents.DeleteDocument(key, etag, out metadataVar, out deletedETag))
  858. {
  859. deleted = true;
  860. actions.Indexing.RemoveAllDocumentReferencesFrom(key);
  861. WorkContext.MarkDeleted(key);
  862. CheckReferenceBecauseOfDocumentUpdate(key, actions);
  863. foreach (var indexName in IndexDefinitionStorage.IndexNames)
  864. {
  865. AbstractViewGenerator abstractViewGenerator =
  866. IndexDefinitionStorage.GetViewGenerator(indexName);
  867. if (abstractViewGenerator == null)
  868. continue;
  869. var token = metadataVar.Value<string>(Constants.RavenEntityName);
  870. if (token != null && // the document has a entity name
  871. abstractViewGenerator.ForEntityNames.Count > 0)
  872. // the index operations on specific entities
  873. {
  874. if (abstractViewGenerator.ForEntityNames.Contains(token) == false)
  875. continue;
  876. }
  877. string indexNameCopy = indexName;
  878. var task = actions.GetTask(x => x.Index == indexNameCopy, new RemoveFromIndexTask
  879. {
  880. Index = indexNameCopy
  881. });
  882. task.Keys.Add(key);
  883. }
  884. if (deletedETag != null)
  885. prefetcher.AfterDelete(key, deletedETag);
  886. DeleteTriggers.Apply(trigger => trigger.AfterDelete(key, null));
  887. }
  888. TransactionalStorage
  889. .ExecuteImmediatelyOrRegisterForSynchronization(() =>
  890. {
  891. DeleteTriggers.Apply(trigger => trigger.AfterCommit(key));
  892. RaiseNotifications(new DocumentChangeNotification
  893. {
  894. Id = key,
  895. Type = DocumentChangeTypes.Delete,
  896. }, metadataVar);
  897. });
  898. }
  899. else
  900. {
  901. var doc = actions.Documents.DocumentMetadataByKey(key, null);
  902. inFlightTransactionalState.DeleteDocumentInTransaction(transactionInformation, key,
  903. etag,
  904. doc == null ? Etag.Empty : doc.Etag,
  905. sequentialUuidGenerator);
  906. deleted = doc != null;
  907. }
  908. workContext.ShouldNotifyAboutWork(() => "DEL " + key);
  909. });
  910. metadata = metadataVar;
  911. return deleted;
  912. }
  913. }
  914. public bool HasTransaction(string txId)
  915. {
  916. return inFlightTransactionalState.HasTransaction(txId);
  917. }
  918. public void PrepareTransaction(string txId)
  919. {
  920. try
  921. {
  922. inFlightTransactionalState.Prepare(txId);
  923. log.Debug("Prepare of tx {0} completed", txId);
  924. }
  925. catch (Exception e)
  926. {
  927. if (TransactionalStorage.HandleException(e))
  928. return;
  929. throw;
  930. }
  931. }
  932. public void Commit(string txId)
  933. {
  934. try
  935. {
  936. using (DocumentLock.Lock())
  937. {
  938. try
  939. {
  940. inFlightTransactionalState.Commit(txId);
  941. log.Debug("Commit of tx {0} completed", txId);
  942. workContext.ShouldNotifyAboutWork(() => "DTC transaction commited");
  943. }
  944. finally
  945. {
  946. inFlightTransactionalState.Rollback(txId); // this is where we actually remove the tx
  947. }
  948. }
  949. }
  950. catch (Exception e)
  951. {
  952. if (TransactionalStorage.HandleException(e))
  953. return;
  954. throw;
  955. }
  956. finally
  957. {
  958. workContext.HandleWorkNotifications();
  959. }
  960. }
  961. public void Rollback(string txId)
  962. {
  963. inFlightTransactionalState.Rollback(txId);
  964. }
  965. [MethodImpl(MethodImplOptions.Synchronized)]
  966. public string PutTransform(string name, TransformerDefinition definition)
  967. {
  968. if (name == null) throw new ArgumentNullException("name");
  969. if (definition == null) throw new ArgumentNullException("definition");
  970. name = name.Trim();
  971. var existingDefinition = IndexDefinitionStorage.GetTransformerDefinition(name);
  972. if (existingDefinition != null && existingDefinition.Equals(definition))
  973. return name; // no op for the same transformer
  974. IndexDefinitionStorage.CreateAndPersistTransform(definition);
  975. IndexDefinitionStorage.AddTransform(name, definition);
  976. return name;
  977. }
  978. // only one index can be created at any given time
  979. // the method already handle attempts to create the same index, so we don't have to
  980. // worry about this.
  981. [MethodImpl(MethodImplOptions.Synchro

Large files files are truncated, but you can click here to view the full file