PageRenderTime 64ms CodeModel.GetById 19ms RepoModel.GetById 1ms 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
  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.Synchronized)]
  982. public string PutIndex(string name, IndexDefinition definition)
  983. {
  984. if (name == null)
  985. throw new ArgumentNullException("name");
  986. var fixedName = IndexDefinitionStorage.FixupIndexName(name);
  987. var existingIndex = IndexDefinitionStorage.GetIndexDefinition(fixedName);
  988. if (existingIndex != null)
  989. {
  990. switch (existingIndex.LockMode)
  991. {
  992. case IndexLockMode.LockedIgnore:
  993. log.Info("Index {0} not saved because it was lock (with ignore)", name);
  994. return name;
  995. case IndexLockMode.LockedError:
  996. throw new InvalidOperationException("Can not overwrite locked index: " + name);
  997. }
  998. }
  999. name = name.Trim();
  1000. switch (FindIndexCreationOptions(definition, ref name))
  1001. {
  1002. case IndexCreationOptions.Noop:
  1003. return name;
  1004. case IndexCreationOptions.Update:
  1005. // ensure that the code can compile
  1006. new DynamicViewCompiler(fixedName, definition, Extensions, IndexDefinitionStorage.IndexDefinitionsPath, Configuration).GenerateInstance();
  1007. DeleteIndex(name);
  1008. break;
  1009. }
  1010. IndexDefinitionStorage.RegisterNewIndexInThisSession(fixedName, definition);
  1011. // this has to happen in this fashion so we will expose the in memory status after the commit, but
  1012. // before the rest of the world is notified about this.
  1013. IndexDefinitionStorage.CreateAndPersistIndex(definition);
  1014. IndexStorage.CreateIndexImplementation(definition);
  1015. InvokeSuggestionIndexing(fixedName, definition);
  1016. TransactionalStorage.Batch(actions =>
  1017. {
  1018. actions.Indexing.AddIndex(fixedName, definition.IsMapReduce);
  1019. workContext.ShouldNotifyAboutWork(() => "PUT INDEX " + name);
  1020. });
  1021. // The act of adding it here make it visible to other threads
  1022. // we have to do it in this way so first we prepare all the elements of the
  1023. // index, then we add it to the storage in a way that make it public
  1024. IndexDefinitionStorage.AddIndex(fixedName, definition);
  1025. workContext.ClearErrorsFor(fixedName);
  1026. TransactionalStorage.ExecuteImmediatelyOrRegisterForSynchronization(() => RaiseNotifications(new IndexChangeNotification
  1027. {
  1028. Name = name,
  1029. Type = IndexChangeTypes.IndexAdded,
  1030. }));
  1031. return name;
  1032. }
  1033. private void InvokeSuggestionIndexing(string name, IndexDefinition definition)
  1034. {
  1035. foreach (var suggestion in definition.Suggestions)
  1036. {
  1037. var field = suggestion.Key;
  1038. var suggestionOption = suggestion.Value;
  1039. if (suggestionOption.Distance == StringDistanceTypes.None)
  1040. continue;
  1041. var indexExtensionKey = MonoHttpUtility.UrlEncode(field + "-" + suggestionOption.Distance + "-" + suggestionOption.Accuracy);
  1042. var suggestionQueryIndexExtension = new SuggestionQueryIndexExtension(
  1043. workContext,
  1044. Path.Combine(configuration.IndexStoragePath, "Raven-Suggestions", name, indexExtensionKey),
  1045. configuration.RunInMemory,
  1046. SuggestionQueryRunner.GetStringDistance(suggestionOption.Distance),
  1047. field,
  1048. suggestionOption.Accuracy);
  1049. IndexStorage.SetIndexExtension(name, indexExtensionKey, suggestionQueryIndexExtension);
  1050. }
  1051. }
  1052. private IndexCreationOptions FindIndexCreationOptions(IndexDefinition definition, ref string name)
  1053. {
  1054. definition.Name = name;
  1055. definition.RemoveDefaultValues();
  1056. IndexDefinitionStorage.ResolveAnalyzers(definition);
  1057. var findIndexCreationOptions = IndexDefinitionStorage.FindIndexCreationOptions(definition);
  1058. return findIndexCreationOptions;
  1059. }
  1060. public QueryResultWithIncludes Query(string index, IndexQuery query, CancellationToken token)
  1061. {
  1062. var list = new List<RavenJObject>();
  1063. var result = Query(index, query, token, null, list.Add);
  1064. result.Results = list;
  1065. return result;
  1066. }
  1067. public QueryResultWithIncludes Query(string index, IndexQuery query, CancellationToken externalCancellationToken, Action<QueryHeaderInformation> headerInfo, Action<RavenJObject> onResult)
  1068. {
  1069. using (var cts = CancellationTokenSource.CreateLinkedTokenSource(externalCancellationToken, workContext.CancellationToken))
  1070. {
  1071. var cancellationToken = cts.Token;
  1072. var queryStat = AddToCurrentlyRunningQueryList(index, query);
  1073. try
  1074. {
  1075. var fixedName = IndexDefinitionStorage.FixupIndexName(index);
  1076. var highlightings = new Dictionary<string, Dictionary<string, string[]>>();
  1077. Func<IndexQueryResult, object> tryRecordHighlighting = queryResult =>
  1078. {
  1079. if (queryResult.Highligtings != null && queryResult.Key != null)
  1080. highlightings.Add(queryResult.Key, queryResult.Highligtings);
  1081. return null;
  1082. };
  1083. var stale = false;
  1084. Tuple<DateTime, Etag> indexTimestamp = Tuple.Create(DateTime.MinValue, Etag.Empty);
  1085. Etag resultEtag = Etag.Empty;
  1086. var nonAuthoritativeInformation = false;
  1087. if (string.IsNullOrEmpty(query.ResultsTransformer) == false)
  1088. {
  1089. query.FieldsToFetch = new[] { Constants.AllFields };
  1090. }
  1091. var duration = Stopwatch.StartNew();
  1092. var idsToLoad = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
  1093. TransactionalStorage.Batch(
  1094. actions =>
  1095. {
  1096. var viewGenerator = IndexDefinitionStorage.GetViewGenerator(fixedName);
  1097. if (viewGenerator == null)
  1098. throw new IndexDoesNotExistsException("Could not find index named: " + index);
  1099. resultEtag = GetIndexEtag(fixedName, null, query.ResultsTransformer);
  1100. stale = actions.Staleness.IsIndexStale(fixedName, query.Cutoff, query.CutoffEtag);
  1101. if (stale == false && query.Cutoff == null && query.CutoffEtag == null)
  1102. {
  1103. var indexInstance = IndexStorage.GetIndexInstance(fixedName);
  1104. stale = stale || (indexInstance != null && indexInstance.IsMapIndexingInProgress);
  1105. }
  1106. indexTimestamp = actions.Staleness.IndexLastUpdatedAt(fixedName);
  1107. var indexFailureInformation = actions.Indexing.GetFailureRate(fixedName);
  1108. if (indexFailureInformation.IsInvalidIndex)
  1109. {
  1110. throw new IndexDisabledException(indexFailureInformation);
  1111. }
  1112. var docRetriever = new DocumentRetriever(actions, ReadTriggers, inFlightTransactionalState, query.QueryInputs, idsToLoad);
  1113. var indexDefinition = GetIndexDefinition(fixedName);
  1114. var fieldsToFetch = new FieldsToFetch(query, viewGenerator.ReduceDefinition == null
  1115. ? Constants.DocumentIdFieldName
  1116. : Constants.ReduceKeyFieldName);
  1117. Func<IndexQueryResult, bool> shouldIncludeInResults =
  1118. result => docRetriever.ShouldIncludeResultInQuery(result, indexDefinition, fieldsToFetch);
  1119. var indexQueryResults = IndexStorage.Query(fixedName, query, shouldIncludeInResults, fieldsToFetch, IndexQueryTriggers, cancellationToken);
  1120. indexQueryResults = new ActiveEnumerable<IndexQueryResult>(indexQueryResults);
  1121. var transformerErrors = new List<string>();
  1122. var results = GetQueryResults(query, viewGenerator, docRetriever,
  1123. from queryResult in indexQueryResults
  1124. let doc = docRetriever.RetrieveDocumentForQuery(queryResult, indexDefinition, fieldsToFetch)
  1125. where doc != null
  1126. let _ = nonAuthoritativeInformation |= (doc.NonAuthoritativeInformation ?? false)
  1127. let __ = tryRecordHighlighting(queryResult)
  1128. select doc, transformerErrors, cancellationToken);
  1129. if (headerInfo != null)
  1130. {
  1131. headerInfo(new QueryHeaderInformation
  1132. {
  1133. Index = index,
  1134. IsStable = stale,
  1135. ResultEtag = resultEtag,
  1136. IndexTimestamp = indexTimestamp.Item1,
  1137. IndexEtag = indexTimestamp.Item2,
  1138. TotalResults = query.TotalSize.Value
  1139. });
  1140. }
  1141. using (new CurrentTransformationScope(docRetriever))
  1142. {
  1143. foreach (var result in results)
  1144. {
  1145. cancellationToken.ThrowIfCancellationRequested();
  1146. onResult(result);
  1147. }
  1148. if (transformerErrors.Count > 0)
  1149. {
  1150. throw new InvalidOperationException("The transform results function failed.\r\n" + string.Join("\r\n", transformerErrors));
  1151. }
  1152. }
  1153. });
  1154. return new QueryResultWithIncludes
  1155. {
  1156. IndexName = index,
  1157. IsStale = stale,
  1158. NonAuthoritativeInformation = nonAuthoritativeInformation,
  1159. SkippedResults = query.SkippedResults.Value,
  1160. TotalResults = query.TotalSize.Value,
  1161. IndexTimestamp = indexTimestamp.Item1,
  1162. IndexEtag = indexTimestamp.Item2,
  1163. ResultEtag = resultEtag,
  1164. IdsToInclude = idsToLoad,
  1165. LastQueryTime = SystemTime.UtcNow,
  1166. Highlightings = highlightings,
  1167. DurationMilliseconds = duration.ElapsedMilliseconds
  1168. };
  1169. }
  1170. finally
  1171. {
  1172. RemoveFromCurrentlyRunningQueryList(index, queryStat);
  1173. }
  1174. }
  1175. }
  1176. private void RemoveFromCurrentlyRunningQueryList(string index, ExecutingQueryInfo queryStat)
  1177. {
  1178. ConcurrentSet<ExecutingQueryInfo> set;
  1179. if(workContext.CurrentlyRunningQueries.TryGetValue(index, out set) == false)
  1180. return;
  1181. set.TryRemove(queryStat);
  1182. }
  1183. private ExecutingQueryInfo AddToCurrentlyRunningQueryList(string index, IndexQuery query)
  1184. {
  1185. var set = workContext.CurrentlyRunningQueries.GetOrAdd(index, x => new ConcurrentSet<ExecutingQueryInfo>());
  1186. var queryStartTime = DateTime.UtcNow;
  1187. var executingQueryInfo = new ExecutingQueryInfo(queryStartTime, query);
  1188. set.Add(executingQueryInfo);
  1189. return executingQueryInfo;
  1190. }
  1191. private IEnumerable<RavenJObject> GetQueryResults(IndexQuery query,
  1192. AbstractViewGenerator viewGenerator,
  1193. DocumentRetriever docRetriever,
  1194. IEnumerable<JsonDocument> results,
  1195. List<string> transformerErrors,
  1196. CancellationToken token)
  1197. {
  1198. if (query.PageSize <= 0) // maybe they just want the stats?
  1199. {
  1200. return Enumerable.Empty<RavenJObject>();
  1201. }
  1202. IndexingFunc transformFunc = null;
  1203. // Check an explicitly declared one first
  1204. if (string.IsNullOrEmpty(query.ResultsTransformer) == false)
  1205. {
  1206. var transformGenerator = IndexDefinitionStorage.GetTransformer(query.ResultsTransformer);
  1207. if (transformGenerator != null && transformGenerator.TransformResultsDefinition != null)
  1208. transformFunc = transformGenerator.TransformResultsDefinition;
  1209. else
  1210. throw new InvalidOperationException("The transformer " + query.ResultsTransformer + " was not found");
  1211. }
  1212. else if (query.SkipTransformResults == false && viewGenerator.TransformResultsDefinition != null)
  1213. {
  1214. transformFunc = source => viewGenerator.TransformResultsDefinition(docRetriever, source);
  1215. }
  1216. if (transformFunc == null)
  1217. return results.Select(x => x.ToJson());
  1218. var dynamicJsonObjects = results.Select(x => new DynamicLuceneOrParentDocumntObject(docRetriever, x.ToJson()));
  1219. var robustEnumerator = new RobustEnumerator(token, 100)
  1220. {
  1221. OnError =
  1222. (exception, o) =>
  1223. transformerErrors.Add(string.Format("Doc '{0}', Error: {1}", Index.TryGetDocKey(o),
  1224. exception.Message))
  1225. };
  1226. return robustEnumerator.RobustEnumeration(
  1227. dynamicJsonObjects.Cast<object>().GetEnumerator(),
  1228. transformFunc)
  1229. .Select(JsonExtensions.ToJObject);
  1230. }
  1231. public IEnumerable<string> QueryDocumentIds(string index, IndexQuery query, CancellationToken token, out bool stale)
  1232. {
  1233. var queryStat = AddToCurrentlyRunningQueryList(index,query);
  1234. try
  1235. {
  1236. bool isStale = false;
  1237. HashSet<string> loadedIds = null;
  1238. TransactionalStorage.Batch(
  1239. actions =>
  1240. {
  1241. isStale = actions.Staleness.IsIndexStale(index, query.Cutoff, null);
  1242. if (isStale == false && query.Cutoff == null)
  1243. {
  1244. var indexInstance = IndexStorage.GetIndexInstance(index);
  1245. isStale = isStale || (indexInstance != null && indexInstance.IsMapIndexingInProgress);
  1246. }
  1247. var indexFailureInformation = actions.Indexing.GetFailureRate(index);
  1248. if (indexFailureInformation.IsInvalidIndex)
  1249. {
  1250. throw new IndexDisabledException(indexFailureInformation);
  1251. }
  1252. loadedIds = new HashSet<string>(from queryResult in IndexStorage.Query(index, query, result => true, new FieldsToFetch(null, AggregationOperation.None, Constants.DocumentIdFieldName), IndexQueryTriggers, token)
  1253. select queryResult.Key);
  1254. });
  1255. stale = isStale;
  1256. return loadedIds;
  1257. }
  1258. finally
  1259. {
  1260. RemoveFromCurrentlyRunningQueryList(index, queryStat);
  1261. }
  1262. }
  1263. public void DeleteTransfom(string name)
  1264. {
  1265. IndexDefinitionStorage.RemoveTransformer(name);
  1266. }
  1267. public void DeleteIndex(string name)
  1268. {
  1269. using (IndexDefinitionStorage.TryRemoveIndexContext())
  1270. {
  1271. var fixedName = IndexDefinitionStorage.FixupIndexName(name);
  1272. IndexDefinitionStorage.RemoveIndex(fixedName);
  1273. IndexStorage.DeleteIndex(fixedName);
  1274. //we may run into a conflict when trying to delete if the index is currently
  1275. //busy indexing documents, worst case scenario, we will have an orphaned index
  1276. //row which will get cleaned up on next db restart.
  1277. for (var i = 0; i < 10; i++)
  1278. {
  1279. try
  1280. {
  1281. TransactionalStorage.Batch(action =>
  1282. {
  1283. action.Indexing.DeleteIndex(fixedName, workContext.CancellationToken);
  1284. workContext.ShouldNotifyAboutWork(() => "DELETE INDEX " + name);
  1285. });
  1286. TransactionalStorage.ExecuteImmediatelyOrRegisterForSynchronization(() => RaiseNotifications(new IndexChangeNotification
  1287. {
  1288. Name = name,
  1289. Type = IndexChangeTypes.IndexRemoved,
  1290. }));
  1291. return;
  1292. }
  1293. catch (ConcurrencyException)
  1294. {
  1295. Thread.Sleep(100);
  1296. }
  1297. }
  1298. ConcurrentSet<string> _;
  1299. workContext.DoNotTouchAgainIfMissingReferences.TryRemove(name, out _);
  1300. workContext.ClearErrorsFor(name);
  1301. }
  1302. }
  1303. public Attachment GetStatic(string name)
  1304. {
  1305. if (name == null)
  1306. throw new ArgumentNullException("name");
  1307. name = name.Trim();
  1308. Attachment attachment = null;
  1309. TransactionalStorage.Batch(actions =>
  1310. {
  1311. attachment = actions.Attachments.GetAttachment(name);
  1312. attachment = ProcessAttachmentReadVetoes(name, attachment);
  1313. ExecuteAttachmentReadTriggers(name, attachment);
  1314. });
  1315. return attachment;
  1316. }
  1317. public IEnumerable<AttachmentInformation> GetStaticsStartingWith(string idPrefix, int start, int pageSize)
  1318. {
  1319. if (idPrefix == null) throw new ArgumentNullException("idPrefix");
  1320. IEnumerable<AttachmentInformation> attachments = null;
  1321. TransactionalStorage.Batch(actions =>
  1322. {
  1323. attachments = actions.Attachments.GetAttachmentsStartingWith(idPrefix, start, pageSize)
  1324. .Select(information =>
  1325. {
  1326. var processAttachmentReadVetoes = ProcessAttachmentReadVetoes(information);
  1327. ExecuteAttachmentReadTriggers(processAttachmentReadVetoes);
  1328. return processAttachmentReadVetoes;
  1329. })
  1330. .Where(x => x != null)
  1331. .ToList();
  1332. });
  1333. return attachments;
  1334. }
  1335. private Attachment ProcessAttachmentReadVetoes(string name, Attachment attachment)
  1336. {
  1337. if (attachment == null)
  1338. return null;
  1339. var foundResult = false;
  1340. foreach (var attachmentReadTriggerLazy in AttachmentReadTriggers)
  1341. {
  1342. if (foundResult)
  1343. break;
  1344. var attachmentReadTrigger = attachmentReadTriggerLazy.Value;
  1345. var readVetoResult = attachmentReadTrigger.AllowRead(name, attachment.Data(), attachment.Metadata,
  1346. ReadOperation.Load);
  1347. switch (readVetoResult.Veto)
  1348. {
  1349. case ReadVetoResult.ReadAllow.Allow:
  1350. break;
  1351. case ReadVetoResult.ReadAllow.Deny:
  1352. attachment.Data = () => new MemoryStream(new byte[0]);
  1353. attachment.Size = 0;
  1354. attachment.Metadata = new RavenJObject
  1355. {
  1356. {
  1357. "Raven-Read-Veto",
  1358. new RavenJObject
  1359. {
  1360. {"Reason", readVetoResult.Reason},
  1361. {"Trigger", attachmentReadTrigger.ToString()}
  1362. }
  1363. }
  1364. };
  1365. foundResult = true;
  1366. break;
  1367. case ReadVetoResult.ReadAllow.Ignore:
  1368. attachment = null;
  1369. foundResult = true;
  1370. break;
  1371. default:
  1372. throw new ArgumentOutOfRangeException(readVetoResult.Veto.ToString());
  1373. }
  1374. }
  1375. return attachment;
  1376. }
  1377. private void ExecuteAttachmentReadTriggers(string name, Attachment attachment)
  1378. {
  1379. if (attachment == null)
  1380. return;
  1381. foreach (var attachmentReadTrigger in AttachmentReadTriggers)
  1382. {
  1383. attachmentReadTrigger.Value.OnRead(name, attachment);
  1384. }
  1385. }
  1386. private AttachmentInformation ProcessAttachmentReadVetoes(AttachmentInformation attachment)
  1387. {
  1388. if (attachment == null)
  1389. return null;
  1390. var foundResult = false;
  1391. foreach (var attachmentReadTriggerLazy in AttachmentReadTriggers)
  1392. {
  1393. if (foundResult)
  1394. break;
  1395. var attachmentReadTrigger = attachmentReadTriggerLazy.Value;
  1396. var readVetoResult = attachmentReadTrigger.AllowRead(attachment.Key, null, attachment.Metadata,
  1397. ReadOperation.Load);
  1398. switch (readVetoResult.Veto)
  1399. {
  1400. case ReadVetoResult.ReadAllow.Allow:
  1401. break;
  1402. case ReadVetoResult.ReadAllow.Deny:
  1403. attachment.Size = 0;
  1404. attachment.Metadata = new RavenJObject
  1405. {
  1406. {
  1407. "Raven-Read-Veto",
  1408. new RavenJObject
  1409. {
  1410. {"Reason", readVetoResult.Reason},
  1411. {"Trigger", attachmentReadTrigger.ToString()}
  1412. }
  1413. }
  1414. };
  1415. foundResult = true;
  1416. break;
  1417. case ReadVetoResult.ReadAllow.Ignore:
  1418. attachment = null;
  1419. foundResult = true;
  1420. break;
  1421. default:
  1422. throw new ArgumentOutOfRangeException(readVetoResult.Veto.ToString());
  1423. }
  1424. }
  1425. return attachment;
  1426. }
  1427. private void ExecuteAttachmentReadTriggers(AttachmentInformation information)
  1428. {
  1429. if (information == null)
  1430. return;
  1431. foreach (var attachmentReadTrigger in AttachmentReadTriggers)
  1432. {
  1433. attachmentReadTrigger.Value.OnRead(information);
  1434. }
  1435. }
  1436. public Etag PutStatic(string name, Etag etag, Stream data, RavenJObject metadata)
  1437. {
  1438. if (name == null)
  1439. throw new ArgumentNullException("name");
  1440. name = name.Trim();
  1441. if (Encoding.Unicode.GetByteCount(name) >= 2048)
  1442. throw new ArgumentException("The key must be a maximum of 2,048 bytes in Unicode, 1,024 characters", "name");
  1443. var locker = putAttachmentSerialLock.GetOrAdd(name, s => new object());
  1444. Monitor.Enter(locker);
  1445. try
  1446. {
  1447. Etag newEtag = Etag.Empty;
  1448. TransactionalStorage.Batch(actions =>
  1449. {
  1450. AssertAttachmentPutOperationNotVetoed(name, metadata, data);
  1451. AttachmentPutTriggers.Apply(trigger => trigger.OnPut(name, data, metadata));
  1452. newEtag = actions.Attachments.AddAttachment(name, etag, data, metadata);
  1453. AttachmentPutTriggers.Apply(trigger => trigger.AfterPut(name, data, metadata, newEtag));
  1454. workContext.ShouldNotifyAboutWork(() => "PUT ATTACHMENT " + name);
  1455. });
  1456. TransactionalStorage
  1457. .ExecuteImmediatelyOrRegisterForSynchronization(() => AttachmentPutTriggers.Apply(trigger => trigger.AfterCommit(name, data, metadata, newEtag)));
  1458. return newEtag;
  1459. }
  1460. finally
  1461. {
  1462. Monitor.Exit(locker);
  1463. putAttachmentSerialLock.TryRemove(name, out locker);
  1464. }
  1465. }
  1466. public void DeleteStatic(string name, Etag etag)
  1467. {
  1468. if (name == null)
  1469. throw new ArgumentNullException("name");
  1470. name = name.Trim();
  1471. TransactionalStorage.Batch(actions =>
  1472. {
  1473. AssertAttachmentDeleteOperationNotVetoed(name);
  1474. AttachmentDeleteTriggers.Apply(x => x.OnDelete(name));
  1475. actions.Attachments.DeleteAttachment(name, etag);
  1476. AttachmentDeleteTriggers.Apply(x => x.AfterDelete(name));
  1477. workContext.ShouldNotifyAboutWork(() => "DELETE ATTACHMENT " + name);
  1478. });
  1479. TransactionalStorage
  1480. .ExecuteImmediatelyOrRegisterForSynchronization(
  1481. () => AttachmentDeleteTriggers.Apply(trigger => trigger.AfterCommit(name)));
  1482. }
  1483. public RavenJArray GetDocumentsWithIdStartingWith(string idPrefix, string matches, string exclude, int start, int pageSize, CancellationToken token)
  1484. {
  1485. var list = new RavenJArray();
  1486. GetDocumentsWithIdStartingWith(idPrefix, matches, exclude, start, pageSize, token, list.Add);
  1487. return list;
  1488. }
  1489. public void GetDocumentsWithIdStartingWith(string idPrefix, string matches, string exclude, int start, int pageSize, CancellationToken token, Action<RavenJObject> addDoc)
  1490. {
  1491. if (idPrefix == null)
  1492. throw new ArgumentNullException("idPrefix");
  1493. idPrefix = idPrefix.Trim();
  1494. TransactionalStorage.Batch(
  1495. actions =>
  1496. {
  1497. var docsToSkip = start;
  1498. var addedDocs = 0;
  1499. var matchedDocs = 0;
  1500. int docCount;
  1501. start = 0;
  1502. do
  1503. {
  1504. docCount = 0;
  1505. var docs = actions.Documents.GetDocumentsWithIdStartingWith(idPrefix, start, pageSize);
  1506. var documentRetriever = new DocumentRetriever(actions, ReadTriggers, inFlightTransactionalState);
  1507. foreach (var doc in docs)
  1508. {
  1509. token.ThrowIfCancellationRequested();
  1510. docCount++;
  1511. var keyTest = doc.Key.Substring(idPrefix.Length);
  1512. if (!WildcardMatcher.Matches(matches, keyTest) || WildcardMatcher.MatchesExclusion(exclude, keyTest))
  1513. continue;
  1514. DocumentRetriever.EnsureIdInMetadata(doc);
  1515. var nonAuthoritativeInformationBehavior = inFlightTransactionalState.GetNonAuthoritativeInformationBehavior<JsonDocument>(null, doc.Key);
  1516. var document = nonAuthoritativeInformationBehavior != null ? nonAuthoritativeInformationBehavior(doc) : doc;
  1517. document = documentRetriever.ExecuteReadTriggers(document, null, ReadOperation.Load);
  1518. if (document == null)
  1519. continue;
  1520. matchedDocs++;
  1521. if (matchedDocs <= docsToSkip)
  1522. continue;
  1523. token.ThrowIfCancellationRequested();
  1524. addDoc(document.ToJson());
  1525. addedDocs++;
  1526. if (addedDocs >= pageSize)
  1527. break;
  1528. }
  1529. start += pageSize;
  1530. }
  1531. while (docCount > 0 && addedDocs < pageSize && start >= 0 && start < int.MaxValue);
  1532. });
  1533. }
  1534. public RavenJArray GetDocuments(int start, int pageSize, Etag etag, CancellationToken token)
  1535. {
  1536. var list = new RavenJArray();
  1537. GetDocuments(start, pageSize, etag, token, list.Add);
  1538. return list;
  1539. }
  1540. public void GetDocuments(int start, int pageSize, Etag etag, CancellationToken token, Action<RavenJObject> addDocument)
  1541. {
  1542. TransactionalStorage.Batch(actions =>
  1543. {
  1544. bool returnedDocs = false;
  1545. while (true)
  1546. {
  1547. var documents = etag == null
  1548. ? actions.Documents.GetDocumentsByReverseUpdateOrder(start, pageSize)
  1549. : actions.Documents.GetDocumentsAfter(etag, pageSize);
  1550. var documentRetriever = new DocumentRetriever(actions, ReadTriggers, inFlightTransactionalState);
  1551. int docCount = 0;
  1552. foreach (var doc in documents)
  1553. {
  1554. docCount++;
  1555. token.ThrowIfCancellationRequested();
  1556. if (etag != null)
  1557. etag = doc.Etag;
  1558. DocumentRetriever.EnsureIdInMetadata(doc);
  1559. var nonAuthoritativeInformationBehavior = inFlightTransactionalState.GetNonAuthoritativeInformationBehavior<JsonDocument>(null, doc.Key);
  1560. var document = nonAuthoritativeInformationBehavior == null ? doc : nonAuthoritativeInformationBehavior(doc);
  1561. document = documentRetriever
  1562. .ExecuteReadTriggers(document, null, ReadOperation.Load);
  1563. if (document == null)
  1564. continue;
  1565. addDocument(document.ToJson());
  1566. returnedDocs = true;
  1567. }
  1568. if (returnedDocs || docCount == 0)
  1569. break;
  1570. start += docCount;
  1571. }
  1572. });
  1573. }
  1574. public AttachmentInformation[] GetAttachments(int start, int pageSize, Etag etag, string startsWith, long maxSize)
  1575. {
  1576. AttachmentInformation[] attachments = null;
  1577. TransactionalStorage.Batch(actions =>
  1578. {
  1579. if (string.IsNullOrEmpty(startsWith) == false)
  1580. attachments = actions.Attachments.GetAttachmentsStartingWith(startsWith, start, pageSize).ToArray();
  1581. else if (etag != null)
  1582. attachments = actions.Attachments.GetAttachmentsAfter(etag, pageSize, maxSize).ToArray();
  1583. else
  1584. attachments = actions.Attachments.GetAttachmentsByReverseUpdateOrder(start).Take(pageSize).ToArray();
  1585. });
  1586. return attachments;
  1587. }
  1588. public RavenJArray GetIndexNames(int start, int pageSize)
  1589. {
  1590. return new RavenJArray(
  1591. IndexDefinitionStorage.IndexNames.Skip(start).Take(pageSize)
  1592. .Select(s => new RavenJValue(s))
  1593. );
  1594. }
  1595. public RavenJArray GetIndexes(int start, int pageSize)
  1596. {
  1597. return new RavenJArray(
  1598. from indexName in IndexDefinitionStorage.IndexNames.Skip(start).Take(pageSize)
  1599. let indexDefinition = IndexDefinitionStorage.GetIndexDefinition(indexName)
  1600. select new RavenJObject
  1601. {
  1602. {"name", new RavenJValue(indexName)},
  1603. {"definition", indexDefinition != null ? RavenJObject.FromObject(indexDefinition) : null},
  1604. });
  1605. }
  1606. public Tuple<PatchResultData, List<string>> ApplyPatch(string docId, Etag etag, ScriptedPatchRequest patch,
  1607. TransactionInformation transactionInformation, bool debugMode = false)
  1608. {
  1609. ScriptedJsonPatcher scriptedJsonPatcher = null;
  1610. var applyPatchInternal = ApplyPatchInternal(docId, etag, transactionInformation,
  1611. (jsonDoc, size) =>
  1612. {
  1613. scriptedJsonPatcher = new ScriptedJsonPatcher(this);
  1614. return scriptedJsonPatcher.Apply(jsonDoc, patch, size, docId);
  1615. },
  1616. () => null,
  1617. () =>
  1618. {
  1619. if (scriptedJsonPatcher == null)
  1620. return null;
  1621. return scriptedJsonPatcher.CreatedDocs;
  1622. }, debugMode);
  1623. return Tuple.Create(applyPatchInternal, scriptedJsonPatcher == null ? new List<string>() : scriptedJsonPatcher.Debug);
  1624. }
  1625. public Tuple<PatchResultData, List<string>> ApplyPatch(string docId, Etag etag,
  1626. ScriptedPatchRequest patchExisting, ScriptedPatchRequest patchDefault, RavenJObject defaultMetadata,
  1627. TransactionInformation transactionInformation, bool debugMode = false)
  1628. {
  1629. ScriptedJsonPatcher scriptedJsonPatcher = null;
  1630. var applyPatchInternal = ApplyPatchInternal(docId, etag, transactionInformation,
  1631. (jsonDoc, size) =>
  1632. {
  1633. scriptedJsonPatcher = new ScriptedJsonPatcher(this);
  1634. return scriptedJsonPatcher.Apply(jsonDoc, patchExisting, size, docId);
  1635. },
  1636. () =>
  1637. {
  1638. if (patchDefault == null)
  1639. return null;
  1640. scriptedJsonPatcher = new ScriptedJsonPatcher(this);
  1641. var jsonDoc = new RavenJObject();
  1642. jsonDoc[Constants.Metadata] = defaultMetadata ?? new RavenJObject();
  1643. return scriptedJsonPatcher.Apply(new RavenJObject(), patchDefault, 0, docId);
  1644. },
  1645. () =>
  1646. {
  1647. if (scriptedJsonPatcher == null)
  1648. return null;
  1649. return scriptedJsonPatcher.CreatedDocs;
  1650. }, debugMode);
  1651. return Tuple.Create(applyPatchInternal, scriptedJsonPatcher == null ? new List<string>() : scriptedJsonPatcher.Debug);
  1652. }
  1653. public PatchResultData ApplyPatch(string docId, Etag etag, PatchRequest[] patchDoc,
  1654. TransactionInformation transactionInformation, bool debugMode = false)
  1655. {
  1656. if (docId == null)
  1657. throw new ArgumentNullException("docId");
  1658. return ApplyPatchInternal(docId, etag, transactionInformation,
  1659. (jsonDoc, size) => new JsonPatcher(jsonDoc).Apply(patchDoc),
  1660. () => null, () => null, debugMode);
  1661. }
  1662. public PatchResultData ApplyPatch(string docId, Etag etag,
  1663. PatchRequest[] patchExistingDoc, PatchRequest[] patchDefaultDoc, RavenJObject defaultMetadata,
  1664. TransactionInformation transactionInformation, bool debugMode = false)
  1665. {
  1666. if (docId == null)
  1667. throw new ArgumentNullException("docId");
  1668. return ApplyPatchInternal(docId, etag, transactionInformation,
  1669. (jsonDoc, size) => new JsonPatcher(jsonDoc).Apply(patchExistingDoc),
  1670. () =>
  1671. {
  1672. if (patchDefaultDoc == null || patchDefaultDoc.Length == 0)
  1673. return null;
  1674. var jsonDoc = new RavenJObject();
  1675. jsonDoc[Constants.Metadata] = defaultMetadata.CloneToken() ?? new RavenJObject();
  1676. return new JsonPatcher(jsonDoc).Apply(patchDefaultDoc);
  1677. },
  1678. () => null, debugMode);
  1679. }
  1680. private PatchResultData ApplyPatchInternal(string docId, Etag etag,
  1681. TransactionInformation transactionInformation,
  1682. Func<RavenJObject, int, RavenJObject> patcher,
  1683. Func<RavenJObject> patcherIfMissing,
  1684. Func<IList<JsonDocument>> getDocsCreatedInPatch,
  1685. bool debugMode)
  1686. {
  1687. if (docId == null) throw new ArgumentNullException("docId");
  1688. docId = docId.Trim();
  1689. var result = new PatchResultData
  1690. {
  1691. PatchResult = PatchResult.Patched
  1692. };
  1693. bool shouldRetry = false;
  1694. int[] retries = { 128 };
  1695. Random rand = null;
  1696. do
  1697. {
  1698. TransactionalStorage.Batch(actions =>
  1699. {
  1700. var doc = actions.Documents.DocumentByKey(docId, transactionInformation);
  1701. log.Debug(() => string.Format("Preparing to apply patch on ({0}). Document found?: {1}.", docId, doc != null));
  1702. if (etag != null && doc != null && doc.Etag != etag)
  1703. {
  1704. Debug.Assert(doc.Etag != null);
  1705. log.Debug(() => string.Format("Got concurrent exception while tried to patch the following document ID: {0}", docId));
  1706. throw new ConcurrencyException("Could not patch document '" + docId + "' because non current etag was used")
  1707. {
  1708. ActualETag = doc.Etag,
  1709. ExpectedETag = etag,
  1710. };
  1711. }
  1712. var jsonDoc = (doc != null ? patcher(doc.ToJson(), doc.SerializedSizeOnDisk) : patcherIfMissing());
  1713. if (jsonDoc == null)
  1714. {
  1715. log.Debug(() => string.Format("Preparing to apply patch on ({0}). DocumentDoesNotExists.", docId));
  1716. result.PatchResult = PatchResult.DocumentDoesNotExists;
  1717. }
  1718. else
  1719. {
  1720. if (debugMode)
  1721. {
  1722. result.Document = jsonDoc;
  1723. result.PatchResult = PatchResult.Tested;
  1724. }
  1725. else
  1726. {
  1727. try
  1728. {
  1729. Put(doc == null ? docId : doc.Key, (doc == null ? null : doc.Etag), jsonDoc, jsonDoc.Value<RavenJObject>(Constants.Metadata), transactionInformation);
  1730. var docsCreatedInPatch = getDocsCreatedInPatch();
  1731. if (docsCreatedInPatch != null && docsCreatedInPatch.Count > 0)
  1732. {
  1733. foreach (var docFromPatch in docsCreatedInPatch)
  1734. {
  1735. Put(docFromPatch.Key, docFromPatch.Etag, docFromPatch.DataAsJson,
  1736. docFromPatch.Metadata, transactionInformation);
  1737. }
  1738. }
  1739. shouldRetry = false;
  1740. result.PatchResult = PatchResult.Patched;
  1741. }
  1742. catch (ConcurrencyException)
  1743. {
  1744. if (actions.IsNested)
  1745. throw;
  1746. if (retries[0]-- > 0)
  1747. {
  1748. shouldRetry = true;
  1749. if (rand == null)
  1750. rand = new Random();
  1751. Thread.Sleep(rand.Next(5, Math.Max(retries[0] * 2, 10)));
  1752. return;
  1753. }
  1754. throw;
  1755. }
  1756. }
  1757. }
  1758. if (shouldRetry == false)
  1759. workContext.ShouldNotifyAboutWork(() => "PATCH " + docId);
  1760. });
  1761. } while (shouldRetry);
  1762. return result;
  1763. }
  1764. public BatchResult[] Batch(IList<ICommandData> commands)
  1765. {
  1766. using (DocumentLock.Lock())
  1767. {
  1768. var shouldRetryIfGotConcurrencyError = commands.All(x => (x is PatchCommandData || x is ScriptedPatchCommandData));
  1769. if (shouldRetryIfGotConcurrencyError)
  1770. {
  1771. var sp = Stopwatch.StartNew();
  1772. var result = BatchWithRetriesOnConcurrencyErrorsAndNoTransactionMerging(commands);
  1773. log.Debug("Successfully executed {0} patch commands in {1}", commands.Count, sp.Elapsed);
  1774. return result;
  1775. }
  1776. BatchResult[] results = null;
  1777. TransactionalStorage.Batch(
  1778. actions =>
  1779. {
  1780. results = ProcessBatch(commands);
  1781. });
  1782. return results;
  1783. }
  1784. }
  1785. private BatchResult[] BatchWithRetriesOnConcurrencyErrorsAndNoTransactionMerging(IList<ICommandData> commands)
  1786. {
  1787. int retries = 128;
  1788. Random rand = null;
  1789. while (true)
  1790. {
  1791. try
  1792. {
  1793. BatchResult[] results = null;
  1794. TransactionalStorage.Batch(_ => results = ProcessBatch(commands));
  1795. return results;
  1796. }
  1797. catch (ConcurrencyException)
  1798. {
  1799. if (retries-- >= 0)
  1800. {
  1801. if(rand == null)
  1802. rand = new Random();
  1803. Thread.Sleep(rand.Next(5, Math.Max(retries * 2, 10)));
  1804. continue;
  1805. }
  1806. throw;
  1807. }
  1808. }
  1809. }
  1810. private BatchResult[] ProcessBatch(IList<ICommandData> commands)
  1811. {
  1812. var results = new BatchResult[commands.Count];
  1813. for (int index = 0; index < commands.Count; index++)
  1814. {
  1815. var command = commands[index];
  1816. results[index] = command.ExecuteBatch(this);
  1817. }
  1818. return results;
  1819. }
  1820. public bool HasTasks
  1821. {
  1822. get
  1823. {
  1824. bool hasTasks = false;
  1825. TransactionalStorage.Batch(actions =>
  1826. {
  1827. hasTasks = actions.Tasks.HasTasks;
  1828. });
  1829. return hasTasks;
  1830. }
  1831. }
  1832. public long ApproximateTaskCount
  1833. {
  1834. get
  1835. {
  1836. long approximateTaskCount = 0;
  1837. TransactionalStorage.Batch(actions =>
  1838. {
  1839. approximateTaskCount = actions.Tasks.ApproximateTaskCount;
  1840. });
  1841. return approximateTaskCount;
  1842. }
  1843. }
  1844. public void StartBackup(string backupDestinationDirectory, bool incrementalBackup, DatabaseDocument databaseDocument)
  1845. {
  1846. var document = Get(BackupStatus.RavenBackupStatusDocumentKey, null);
  1847. if (document != null)
  1848. {
  1849. var backupStatus = document.DataAsJson.JsonDeserialization<BackupStatus>();
  1850. if (backupStatus.IsRunning)
  1851. {
  1852. throw new InvalidOperationException("Backup is already running");
  1853. }
  1854. }
  1855. bool circularLogging;
  1856. if (incrementalBackup &&
  1857. TransactionalStorage is Raven.Storage.Esent.TransactionalStorage &&
  1858. (bool.TryParse(Configuration.Settings["Raven/Esent/CircularLog"], out circularLogging) == false || circularLogging))
  1859. {
  1860. throw new InvalidOperationException("In order to run incremental backups using Esent you must have circular logging disabled");
  1861. }
  1862. Put(BackupStatus.RavenBackupStatusDocumentKey, null, RavenJObject.FromObject(new BackupStatus
  1863. {
  1864. Started = SystemTime.UtcNow,
  1865. IsRunning = true,
  1866. }), new RavenJObject(), null);
  1867. IndexStorage.FlushMapIndexes();
  1868. IndexStorage.FlushReduceIndexes();
  1869. TransactionalStorage.StartBackupOperation(this, backupDestinationDirectory, incrementalBackup, databaseDocument);
  1870. }
  1871. public static void Restore(RavenConfiguration configuration, string backupLocation, string databaseLocation, Action<string> output, bool defrag)
  1872. {
  1873. using (var transactionalStorage = configuration.CreateTransactionalStorage(() => { }))
  1874. {
  1875. if (!string.IsNullOrWhiteSpace(databaseLocation))
  1876. {
  1877. configuration.DataDirectory = databaseLocation;
  1878. }
  1879. transactionalStorage.Restore(backupLocation, databaseLocation, output, defrag);
  1880. }
  1881. }
  1882. public void ResetIndex(string index)
  1883. {
  1884. var indexDefinition = IndexDefinitionStorage.GetIndexDefinition(index);
  1885. if (indexDefinition == null)
  1886. throw new InvalidOperationException("There is no index named: " + index);
  1887. DeleteIndex(index);
  1888. PutIndex(index, indexDefinition);
  1889. }
  1890. public IndexDefinition GetIndexDefinition(string index)
  1891. {
  1892. return IndexDefinitionStorage.GetIndexDefinition(index);
  1893. }
  1894. static string buildVersion;
  1895. public static string BuildVersion
  1896. {
  1897. get
  1898. {
  1899. return buildVersion ??
  1900. (buildVersion = GetBuildVersion().ToString(CultureInfo.InvariantCulture));
  1901. }
  1902. }
  1903. private static int GetBuildVersion()
  1904. {
  1905. var fileVersionInfo = FileVersionInfo.GetVersionInfo(typeof(DocumentDatabase).Assembly.Location);
  1906. if (fileVersionInfo.FilePrivatePart != 0)
  1907. return fileVersionInfo.FilePrivatePart;
  1908. return fileVersionInfo.FileBuildPart;
  1909. }
  1910. private volatile bool disposed;
  1911. private readonly ValidateLicense validateLicense;
  1912. public string ServerUrl
  1913. {
  1914. get
  1915. {
  1916. var serverUrl = Configuration.ServerUrl;
  1917. if (string.IsNullOrEmpty(Name))
  1918. return serverUrl;
  1919. if (serverUrl.EndsWith("/"))
  1920. return serverUrl + "databases/" + Name;
  1921. return serverUrl + "/databases/" + Name;
  1922. }
  1923. }
  1924. static string productVersion;
  1925. private readonly SequentialUuidGenerator sequentialUuidGenerator;
  1926. private readonly TransportState transportState;
  1927. private DisposableAction exitDocumentSerialLock;
  1928. public static string ProductVersion
  1929. {
  1930. get
  1931. {
  1932. return productVersion ??
  1933. (productVersion = FileVersionInfo.GetVersionInfo(typeof(DocumentDatabase).Assembly.Location).ProductVersion);
  1934. }
  1935. }
  1936. public string[] GetIndexFields(string index)
  1937. {
  1938. var abstractViewGenerator = IndexDefinitionStorage.GetViewGenerator(index);
  1939. if (abstractViewGenerator == null)
  1940. return new string[0];
  1941. return abstractViewGenerator.Fields;
  1942. }
  1943. /// <summary>
  1944. /// This API is provided solely for the use of bundles that might need to run
  1945. /// without any other bundle interfering. Specifically, the replication bundle
  1946. /// need to be able to run without interference from any other bundle.
  1947. /// </summary>
  1948. /// <returns></returns>
  1949. public IDisposable DisableAllTriggersForCurrentThread()
  1950. {
  1951. if (disposed)
  1952. return new DisposableAction(() => { });
  1953. var old = disableAllTriggers.Value;
  1954. disableAllTriggers.Value = true;
  1955. return new DisposableAction(() =>
  1956. {
  1957. if (disposed)
  1958. return;
  1959. try
  1960. {
  1961. disableAllTriggers.Value = old;
  1962. }
  1963. catch (ObjectDisposedException)
  1964. {
  1965. }
  1966. });
  1967. }
  1968. /// <summary>
  1969. /// Whatever this database has been disposed
  1970. /// </summary>
  1971. public bool Disposed
  1972. {
  1973. get { return disposed; }
  1974. }
  1975. public TransportState TransportState
  1976. {
  1977. get
  1978. {
  1979. return transportState;
  1980. }
  1981. }
  1982. /// <summary>
  1983. /// Get the total index storage size taken by the indexes on the disk.
  1984. /// This explicitly does NOT include in memory indexes.
  1985. /// </summary>
  1986. /// <remarks>
  1987. /// This is a potentially a very expensive call, avoid making it if possible.
  1988. /// </remarks>
  1989. public long GetIndexStorageSizeOnDisk()
  1990. {
  1991. if (Configuration.RunInMemory)
  1992. return 0;
  1993. var indexes = Directory.GetFiles(Configuration.IndexStoragePath, "*.*", SearchOption.AllDirectories);
  1994. var totalIndexSize = indexes.Sum(file =>
  1995. {
  1996. try
  1997. {
  1998. return new FileInfo(file).Length;
  1999. }
  2000. catch (UnauthorizedAccessException)
  2001. {
  2002. return 0;
  2003. }
  2004. catch (FileNotFoundException)
  2005. {
  2006. return 0;
  2007. }
  2008. });
  2009. return totalIndexSize;
  2010. }
  2011. /// <summary>
  2012. /// Get the total size taken by the database on the disk.
  2013. /// This explicitly does NOT include in memory database.
  2014. /// It does include any reserved space on the file system, which may significantly increase
  2015. /// the database size.
  2016. /// </summary>
  2017. /// <remarks>
  2018. /// This is a potentially a very expensive call, avoid making it if possible.
  2019. /// </remarks>
  2020. public long GetTransactionalStorageSizeOnDisk()
  2021. {
  2022. return Configuration.RunInMemory ? 0 : TransactionalStorage.GetDatabaseSizeInBytes();
  2023. }
  2024. /// <summary>
  2025. /// Get the total size taken by the database on the disk.
  2026. /// This explicitly does NOT include in memory indexes or in memory database.
  2027. /// It does include any reserved space on the file system, which may significantly increase
  2028. /// the database size.
  2029. /// </summary>
  2030. /// <remarks>
  2031. /// This is a potentially a very expensive call, avoid making it if possible.
  2032. /// </remarks>
  2033. public long GetTotalSizeOnDisk()
  2034. {
  2035. if (Configuration.RunInMemory)
  2036. return 0;
  2037. return GetIndexStorageSizeOnDisk() + GetTransactionalStorageSizeOnDisk();
  2038. }
  2039. public Etag GetIndexEtag(string indexName, Etag previousEtag, string resultTransformer = null)
  2040. {
  2041. var fixedName = IndexDefinitionStorage.FixupIndexName(indexName);
  2042. Etag lastDocEtag = Etag.Empty;
  2043. Etag lastReducedEtag = null;
  2044. bool isStale = false;
  2045. int touchCount = 0;
  2046. TransactionalStorage.Batch(accessor =>
  2047. {
  2048. var indexInstance = IndexStorage.GetIndexInstance(fixedName);
  2049. isStale = (indexInstance != null && indexInstance.IsMapIndexingInProgress) ||
  2050. accessor.Staleness.IsIndexStale(fixedName, null, null);
  2051. lastDocEtag = accessor.Staleness.GetMostRecentDocumentEtag();
  2052. var indexStats = accessor.Indexing.GetIndexStats(fixedName);
  2053. if (indexStats != null)
  2054. {
  2055. lastReducedEtag = indexStats.LastReducedEtag;
  2056. }
  2057. touchCount = accessor.Staleness.GetIndexTouchCount(fixedName);
  2058. });
  2059. var indexDefinition = GetIndexDefinition(fixedName);
  2060. if (indexDefinition == null)
  2061. return Etag.Empty; // this ensures that we will get the normal reaction of IndexNotFound later on.
  2062. using (var md5 = MD5.Create())
  2063. {
  2064. var list = new List<byte>();
  2065. list.AddRange(indexDefinition.GetIndexHash());
  2066. list.AddRange(Encoding.Unicode.GetBytes(indexName));
  2067. if (string.IsNullOrWhiteSpace(resultTransformer) == false)
  2068. {
  2069. var abstractTransformer = IndexDefinitionStorage.GetTransformer(resultTransformer);
  2070. if (abstractTransformer == null)
  2071. throw new InvalidOperationException("The result transformer: " + resultTransformer + " was not found");
  2072. list.AddRange(abstractTransformer.GetHashCodeBytes());
  2073. }
  2074. list.AddRange(lastDocEtag.ToByteArray());
  2075. list.AddRange(BitConverter.GetBytes(touchCount));
  2076. list.AddRange(BitConverter.GetBytes(isStale));
  2077. if (lastReducedEtag != null)
  2078. {
  2079. list.AddRange(lastReducedEtag.ToByteArray());
  2080. }
  2081. var indexEtag = Etag.Parse(md5.ComputeHash(list.ToArray()));
  2082. if (previousEtag != null && previousEtag != indexEtag)
  2083. {
  2084. // the index changed between the time when we got it and the time
  2085. // we actually call this, we need to return something random so that
  2086. // the next time we won't get 304
  2087. return Etag.InvalidEtag;
  2088. }
  2089. return indexEtag;
  2090. }
  2091. }
  2092. public int BulkInsert(BulkInsertOptions options, IEnumerable<IEnumerable<JsonDocument>> docBatches, Guid operationId)
  2093. {
  2094. var documents = 0;
  2095. TransactionalStorage.Batch(accessor =>
  2096. {
  2097. RaiseNotifications(new BulkInsertChangeNotification
  2098. {
  2099. OperationId = operationId,
  2100. Type = DocumentChangeTypes.BulkInsertStarted
  2101. });
  2102. foreach (var docs in docBatches)
  2103. {
  2104. WorkContext.CancellationToken.ThrowIfCancellationRequested();
  2105. using (DocumentLock.Lock())
  2106. {
  2107. var inserts = 0;
  2108. var batch = 0;
  2109. var keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
  2110. var docsToInsert = docs.ToArray();
  2111. foreach (var doc in docsToInsert)
  2112. {
  2113. try
  2114. {
  2115. RemoveReservedProperties(doc.DataAsJson);
  2116. RemoveMetadataReservedProperties(doc.Metadata);
  2117. if (options.CheckReferencesInIndexes) keys.Add(doc.Key);
  2118. documents++;
  2119. batch++;
  2120. AssertPutOperationNotVetoed(doc.Key, doc.Metadata, doc.DataAsJson, null);
  2121. foreach (var trigger in PutTriggers)
  2122. {
  2123. trigger.Value.OnPut(doc.Key, doc.DataAsJson, doc.Metadata, null);
  2124. }
  2125. var result = accessor.Documents.InsertDocument(doc.Key, doc.DataAsJson, doc.Metadata, options.CheckForUpdates);
  2126. if (result.Updated == false) inserts++;
  2127. doc.Etag = result.Etag;
  2128. doc.Metadata.EnsureSnapshot(
  2129. "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?");
  2130. doc.DataAsJson.EnsureSnapshot(
  2131. "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?");
  2132. foreach (var trigger in PutTriggers)
  2133. {
  2134. trigger.Value.AfterPut(doc.Key, doc.DataAsJson, doc.Metadata, result.Etag, null);
  2135. }
  2136. }
  2137. catch (Exception e)
  2138. {
  2139. RaiseNotifications(
  2140. new BulkInsertChangeNotification
  2141. {
  2142. OperationId = operationId,
  2143. Message = e.Message,
  2144. Etag = doc.Etag,
  2145. Id = doc.Key,
  2146. Type = DocumentChangeTypes.BulkInsertError
  2147. });
  2148. throw;
  2149. }
  2150. }
  2151. if (options.CheckReferencesInIndexes)
  2152. {
  2153. foreach (var key in keys)
  2154. {
  2155. CheckReferenceBecauseOfDocumentUpdate(key, accessor);
  2156. }
  2157. }
  2158. accessor.Documents.IncrementDocumentCount(inserts);
  2159. accessor.General.PulseTransaction();
  2160. workContext.ShouldNotifyAboutWork(() => "BulkInsert batch of " + batch + " docs");
  2161. workContext.NotifyAboutWork(); // forcing notification so we would start indexing right away
  2162. }
  2163. }
  2164. RaiseNotifications(new BulkInsertChangeNotification
  2165. {
  2166. OperationId = operationId,
  2167. Type = DocumentChangeTypes.BulkInsertEnded
  2168. });
  2169. if (documents == 0)
  2170. return;
  2171. workContext.ShouldNotifyAboutWork(() => "BulkInsert of " + documents + " docs");
  2172. });
  2173. return documents;
  2174. }
  2175. public TouchedDocumentInfo GetRecentTouchesFor(string key)
  2176. {
  2177. TouchedDocumentInfo info;
  2178. recentTouches.TryGetValue(key, out info);
  2179. return info;
  2180. }
  2181. public void AddTask(Task task, object state, out long id)
  2182. {
  2183. if (task.Status == TaskStatus.Created)
  2184. throw new ArgumentException("Task must be started before it gets added to the database.", "task");
  2185. var localId = id = Interlocked.Increment(ref pendingTaskCounter);
  2186. pendingTasks.TryAdd(localId, new PendingTaskAndState
  2187. {
  2188. Task = task,
  2189. State = state
  2190. });
  2191. }
  2192. public object GetTaskState(long id)
  2193. {
  2194. PendingTaskAndState value;
  2195. if (pendingTasks.TryGetValue(id, out value))
  2196. {
  2197. if (value.Task.IsFaulted || value.Task.IsCanceled)
  2198. value.Task.Wait(); //throws
  2199. return value.State;
  2200. }
  2201. return null;
  2202. }
  2203. public RavenJArray GetTransformerNames(int start, int pageSize)
  2204. {
  2205. return new RavenJArray(
  2206. IndexDefinitionStorage.TransformerNames.Skip(start).Take(pageSize)
  2207. .Select(s => new RavenJValue(s))
  2208. );
  2209. }
  2210. public RavenJArray GetTransformers(int start, int pageSize)
  2211. {
  2212. return new RavenJArray(
  2213. IndexDefinitionStorage.TransformerNames.Skip(start).Take(pageSize)
  2214. .Select(
  2215. indexName => new RavenJObject
  2216. {
  2217. {"name", new RavenJValue(indexName) },
  2218. {"definition", RavenJObject.FromObject(IndexDefinitionStorage.GetTransformerDefinition(indexName))}
  2219. }));
  2220. }
  2221. public JsonDocument GetWithTransformer(string key, string transformer, TransactionInformation transactionInformation, Dictionary<string, RavenJToken> queryInputs)
  2222. {
  2223. JsonDocument result = null;
  2224. TransactionalStorage.Batch(
  2225. actions =>
  2226. {
  2227. var docRetriever = new DocumentRetriever(actions, ReadTriggers, inFlightTransactionalState, queryInputs);
  2228. using (new CurrentTransformationScope(docRetriever))
  2229. {
  2230. var document = Get(key, transactionInformation);
  2231. if (document == null)
  2232. return;
  2233. if (document.Metadata.ContainsKey("Raven-Read-Veto"))
  2234. {
  2235. result = document;
  2236. return;
  2237. }
  2238. var storedTransformer = IndexDefinitionStorage.GetTransformer(transformer);
  2239. if (storedTransformer == null)
  2240. throw new InvalidOperationException("No transformer with the name: " + transformer);
  2241. var transformed = storedTransformer.TransformResultsDefinition(new[] { new DynamicJsonObject(document.ToJson()) })
  2242. .Select(x => JsonExtensions.ToJObject(x))
  2243. .ToArray();
  2244. if (transformed.Length == 0)
  2245. return;
  2246. result = new JsonDocument
  2247. {
  2248. Etag = document.Etag.HashWith(storedTransformer.GetHashCodeBytes()).HashWith(docRetriever.Etag),
  2249. NonAuthoritativeInformation = document.NonAuthoritativeInformation,
  2250. LastModified = document.LastModified,
  2251. DataAsJson = new RavenJObject {{"$values", new RavenJArray(transformed)}},
  2252. };
  2253. }
  2254. });
  2255. return result;
  2256. }
  2257. public TransformerDefinition GetTransformerDefinition(string name)
  2258. {
  2259. return IndexDefinitionStorage.GetTransformerDefinition(name);
  2260. }
  2261. }
  2262. }