PageRenderTime 53ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/Raven.Database/Storage/IndexDefinitionStorage.cs

https://github.com/nwendel/ravendb
C# | 547 lines | 456 code | 84 blank | 7 comment | 43 complexity | 27c0134c04b5f2d5f8e2a37ec4b300fc MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, BSD-3-Clause, CC-BY-SA-3.0
  1. //-----------------------------------------------------------------------
  2. // <copyright file="IndexDefinitionStorage.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;
  10. using System.IO;
  11. using System.Linq;
  12. using System.Security.Cryptography;
  13. using System.Text;
  14. using System.Threading;
  15. using Lucene.Net.Analysis.Standard;
  16. using Lucene.Net.Documents;
  17. using Microsoft.Isam.Esent.Interop;
  18. using Mono.CSharp;
  19. using Mono.CSharp.Linq;
  20. using Raven.Abstractions.Data;
  21. using Raven.Abstractions.Logging;
  22. using Raven.Database.Indexing.IndexMerging;
  23. using Raven.Imports.Newtonsoft.Json;
  24. using Raven.Abstractions;
  25. using Raven.Abstractions.Extensions;
  26. using Raven.Abstractions.Indexing;
  27. using Raven.Abstractions.MEF;
  28. using Raven.Database.Config;
  29. using Raven.Database.Extensions;
  30. using Raven.Database.Linq;
  31. using Raven.Database.Plugins;
  32. using Raven.Database.Indexing;
  33. using CSharpParser = ICSharpCode.NRefactory.CSharp.CSharpParser;
  34. using Expression = ICSharpCode.NRefactory.CSharp.Expression;
  35. namespace Raven.Database.Storage
  36. {
  37. public class IndexDefinitionStorage
  38. {
  39. private const string IndexDefDir = "IndexDefinitions";
  40. private readonly ReaderWriterLockSlim currentlyIndexingLock = new ReaderWriterLockSlim();
  41. public long currentlyIndexing;
  42. private readonly ConcurrentDictionary<int, AbstractViewGenerator> indexCache =
  43. new ConcurrentDictionary<int, AbstractViewGenerator>();
  44. private readonly ConcurrentDictionary<int, AbstractTransformer> transformCache =
  45. new ConcurrentDictionary<int, AbstractTransformer>();
  46. private readonly ConcurrentDictionary<string, int> indexNameToId =
  47. new ConcurrentDictionary<string, int>(StringComparer.OrdinalIgnoreCase);
  48. private readonly ConcurrentDictionary<string, int> transformNameToId =
  49. new ConcurrentDictionary<string, int>(StringComparer.OrdinalIgnoreCase);
  50. private readonly ConcurrentDictionary<int, TransformerDefinition> transformDefinitions =
  51. new ConcurrentDictionary<int, TransformerDefinition>();
  52. private readonly ConcurrentDictionary<int, IndexDefinition> indexDefinitions =
  53. new ConcurrentDictionary<int, IndexDefinition>();
  54. private readonly ConcurrentDictionary<int, IndexDefinition> newDefinitionsThisSession =
  55. new ConcurrentDictionary<int, IndexDefinition>();
  56. private static readonly ILog logger = LogManager.GetCurrentClassLogger();
  57. private readonly string path;
  58. private readonly InMemoryRavenConfiguration configuration;
  59. private readonly ITransactionalStorage transactionalStorage;
  60. private readonly OrderedPartCollection<AbstractDynamicCompilationExtension> extensions;
  61. private List<IndexMergeSuggestion> IndexMergeSuggestions { get; set; }
  62. public IndexDefinitionStorage(
  63. InMemoryRavenConfiguration configuration,
  64. ITransactionalStorage transactionalStorage,
  65. string path,
  66. IEnumerable<AbstractViewGenerator> compiledGenerators,
  67. OrderedPartCollection<AbstractDynamicCompilationExtension> extensions)
  68. {
  69. IndexMergeSuggestions = new List<IndexMergeSuggestion>();
  70. this.configuration = configuration;
  71. this.transactionalStorage = transactionalStorage;
  72. this.extensions = extensions; // this is used later in the ctor, so it must appears first
  73. this.path = Path.Combine(path, IndexDefDir);
  74. if (Directory.Exists(this.path) == false && configuration.RunInMemory == false)
  75. Directory.CreateDirectory(this.path);
  76. if (configuration.RunInMemory == false)
  77. ReadFromDisk();
  78. newDefinitionsThisSession.Clear();
  79. }
  80. public bool IsNewThisSession(IndexDefinition definition)
  81. {
  82. return newDefinitionsThisSession.ContainsKey(definition.IndexId);
  83. }
  84. private void ReadFromDisk()
  85. {
  86. foreach (var indexDefinition in ReadIndexDefinitionsFromDisk())
  87. {
  88. try
  89. {
  90. if (Contains(indexDefinition.Name)) // checking if there are no older indexes with the same name
  91. RemoveIndexAndCleanup(indexDefinition.Name);
  92. ResolveAnalyzers(indexDefinition);
  93. AddAndCompileIndex(indexDefinition);
  94. AddIndex(indexDefinition.IndexId, indexDefinition);
  95. }
  96. catch (Exception e)
  97. {
  98. logger.WarnException(string.Format("Could not compile index '{0} ({1})', skipping bad index", indexDefinition.IndexId, indexDefinition.Name), e);
  99. }
  100. }
  101. foreach (var transformerDefinition in ReadTransformerDefinitionsFromDisk())
  102. {
  103. try
  104. {
  105. RemoveTransformer(transformerDefinition.Name);
  106. AddAndCompileTransform(transformerDefinition);
  107. AddTransform(transformerDefinition.TransfomerId, transformerDefinition);
  108. }
  109. catch (Exception e)
  110. {
  111. logger.WarnException(string.Format("Could not compile transformer '{0} ({1})', skipping bad transformer", transformerDefinition.TransfomerId, transformerDefinition.Name), e);
  112. }
  113. }
  114. }
  115. private IEnumerable<IndexDefinition> ReadIndexDefinitionsFromDisk()
  116. {
  117. var result = new SortedList<int, IndexDefinition>();
  118. foreach (var index in Directory.GetFiles(path, "*.index"))
  119. {
  120. try
  121. {
  122. var indexDefinition = JsonConvert.DeserializeObject<IndexDefinition>(File.ReadAllText(index), Default.Converters);
  123. result.Add(indexDefinition.IndexId, indexDefinition);
  124. }
  125. catch (Exception e)
  126. {
  127. logger.WarnException("Could not compile index " + index + ", skipping bad index", e);
  128. }
  129. }
  130. return result.Values;
  131. }
  132. private IEnumerable<TransformerDefinition> ReadTransformerDefinitionsFromDisk()
  133. {
  134. var result = new SortedList<int, TransformerDefinition>();
  135. foreach (var transformer in Directory.GetFiles(path, "*.transform"))
  136. {
  137. try
  138. {
  139. var transformerDefinition = JsonConvert.DeserializeObject<TransformerDefinition>(File.ReadAllText(transformer), Default.Converters);
  140. result.Add(transformerDefinition.TransfomerId, transformerDefinition);
  141. }
  142. catch (Exception e)
  143. {
  144. logger.WarnException("Could not compile transformer " + transformer + ", skipping bad transformer", e);
  145. }
  146. }
  147. return result.Values;
  148. }
  149. private void RemoveIndexAndCleanup(string name)
  150. {
  151. var index = GetIndexDefinition(name);
  152. if (index == null)
  153. return;
  154. transactionalStorage.Batch(accessor =>
  155. {
  156. accessor.Indexing.PrepareIndexForDeletion(index.IndexId);
  157. accessor.Indexing.DeleteIndex(index.IndexId, CancellationToken.None);
  158. });
  159. RemoveIndex(index.IndexId);
  160. }
  161. public int IndexesCount
  162. {
  163. get { return indexCache.Count; }
  164. }
  165. public string[] IndexNames
  166. {
  167. get { return IndexDefinitions.Values.OrderBy(x => x.Name).Select(x => x.Name).ToArray(); }
  168. }
  169. public int[] Indexes
  170. {
  171. get { return indexCache.Keys.OrderBy(name => name).ToArray(); }
  172. }
  173. public string IndexDefinitionsPath
  174. {
  175. get { return path; }
  176. }
  177. public string[] TransformerNames
  178. {
  179. get
  180. {
  181. return transformDefinitions.Values
  182. .OrderBy(x => x.Name)
  183. .Select(x => x.Name)
  184. .ToArray();
  185. }
  186. }
  187. public ConcurrentDictionary<int, IndexDefinition> IndexDefinitions
  188. {
  189. get { return indexDefinitions; }
  190. }
  191. public string CreateAndPersistIndex(IndexDefinition indexDefinition)
  192. {
  193. var transformer = AddAndCompileIndex(indexDefinition);
  194. if (configuration.RunInMemory == false)
  195. {
  196. WriteIndexDefinition(indexDefinition);
  197. }
  198. return transformer.Name;
  199. }
  200. private void WriteIndexDefinition(IndexDefinition indexDefinition)
  201. {
  202. if (configuration.RunInMemory)
  203. return;
  204. var indexName = Path.Combine(path, indexDefinition.IndexId + ".index");
  205. File.WriteAllText(indexName,
  206. JsonConvert.SerializeObject(indexDefinition, Formatting.Indented, Default.Converters));
  207. }
  208. private void WriteTransformerDefinition(TransformerDefinition transformerDefinition)
  209. {
  210. if (configuration.RunInMemory)
  211. return;
  212. string transformerFileName = Path.Combine(path, transformerDefinition.TransfomerId + ".transform");
  213. File.WriteAllText(transformerFileName,
  214. JsonConvert.SerializeObject(transformerDefinition, Formatting.Indented, Default.Converters));
  215. }
  216. public string CreateAndPersistTransform(TransformerDefinition transformerDefinition)
  217. {
  218. var transformer = AddAndCompileTransform(transformerDefinition);
  219. if (configuration.RunInMemory == false)
  220. {
  221. WriteTransformerDefinition(transformerDefinition);
  222. }
  223. return transformer.Name;
  224. }
  225. public void UpdateIndexDefinitionWithoutUpdatingCompiledIndex(IndexDefinition definition)
  226. {
  227. IndexDefinitions.AddOrUpdate(definition.IndexId, s =>
  228. {
  229. throw new InvalidOperationException(
  230. "Cannot find index named: " +
  231. definition.IndexId);
  232. }, (s, indexDefinition) => definition);
  233. WriteIndexDefinition(definition);
  234. }
  235. private DynamicViewCompiler AddAndCompileIndex(IndexDefinition indexDefinition)
  236. {
  237. var transformer = new DynamicViewCompiler(indexDefinition.Name, indexDefinition, extensions, path,
  238. configuration);
  239. var generator = transformer.GenerateInstance();
  240. indexCache.AddOrUpdate(indexDefinition.IndexId, generator, (s, viewGenerator) => generator);
  241. logger.Info("New index {0}:\r\n{1}\r\nCompiled to:\r\n{2}", transformer.Name, transformer.CompiledQueryText,
  242. transformer.CompiledQueryText);
  243. return transformer;
  244. }
  245. private DynamicTransformerCompiler AddAndCompileTransform(TransformerDefinition transformerDefinition)
  246. {
  247. var transformer = new DynamicTransformerCompiler(transformerDefinition, configuration, extensions,
  248. transformerDefinition.Name, path);
  249. var generator = transformer.GenerateInstance();
  250. transformCache.AddOrUpdate(transformerDefinition.TransfomerId, generator, (s, viewGenerator) => generator);
  251. logger.Info("New transformer {0}:\r\n{1}\r\nCompiled to:\r\n{2}", transformer.Name,
  252. transformer.CompiledQueryText,
  253. transformer.CompiledQueryText);
  254. return transformer;
  255. }
  256. public void RegisterNewIndexInThisSession(string name, IndexDefinition definition)
  257. {
  258. newDefinitionsThisSession.TryAdd(definition.IndexId, definition);
  259. }
  260. public void AddIndex(int id, IndexDefinition definition)
  261. {
  262. IndexDefinitions.AddOrUpdate(id, definition, (s1, def) =>
  263. {
  264. if (def.IsCompiled)
  265. throw new InvalidOperationException("Index " + id + " is a compiled index, and cannot be replaced");
  266. return definition;
  267. });
  268. indexNameToId[definition.Name] = id;
  269. UpdateIndexMappingFile();
  270. }
  271. public void AddTransform(int id, TransformerDefinition definition)
  272. {
  273. transformDefinitions.AddOrUpdate(id, definition, (s1, def) => definition);
  274. transformNameToId[definition.Name] = id;
  275. UpdateTransformerMappingFile();
  276. }
  277. public void RemoveIndex(int id)
  278. {
  279. AbstractViewGenerator ignoredViewGenerator;
  280. int ignoredId;
  281. if (indexCache.TryRemove(id, out ignoredViewGenerator))
  282. indexNameToId.TryRemove(ignoredViewGenerator.Name, out ignoredId);
  283. IndexDefinition ignoredIndexDefinition;
  284. IndexDefinitions.TryRemove(id, out ignoredIndexDefinition);
  285. newDefinitionsThisSession.TryRemove(id, out ignoredIndexDefinition);
  286. if (configuration.RunInMemory)
  287. return;
  288. File.Delete(GetIndexSourcePath(id) + ".index");
  289. UpdateIndexMappingFile();
  290. }
  291. private string GetIndexSourcePath(int id)
  292. {
  293. return Path.Combine(path, id.ToString());
  294. }
  295. public IndexDefinition GetIndexDefinition(string name)
  296. {
  297. int id = 0;
  298. if (indexNameToId.TryGetValue(name, out id))
  299. {
  300. IndexDefinition indexDefinition = IndexDefinitions[id];
  301. if (indexDefinition.Fields.Count == 0)
  302. {
  303. AbstractViewGenerator abstractViewGenerator = GetViewGenerator(id);
  304. indexDefinition.Fields = abstractViewGenerator.Fields;
  305. }
  306. return indexDefinition;
  307. }
  308. return null;
  309. }
  310. public IndexDefinition GetIndexDefinition(int id)
  311. {
  312. IndexDefinition value;
  313. IndexDefinitions.TryGetValue(id, out value);
  314. return value;
  315. }
  316. public TransformerDefinition GetTransformerDefinition(string name)
  317. {
  318. int id;
  319. if (transformNameToId.TryGetValue(name, out id))
  320. return transformDefinitions[id];
  321. return null;
  322. }
  323. public IndexMergeResults ProposeIndexMergeSuggestions()
  324. {
  325. var indexMerger = new IndexMerger(IndexDefinitions.ToDictionary(x=>x.Key,x=>x.Value));
  326. return indexMerger.ProposeIndexMergeSuggestions();
  327. }
  328. public TransformerDefinition GetTransformerDefinition(int id)
  329. {
  330. TransformerDefinition value;
  331. transformDefinitions.TryGetValue(id, out value);
  332. return value;
  333. }
  334. public AbstractViewGenerator GetViewGenerator(string name)
  335. {
  336. int id = 0;
  337. if (indexNameToId.TryGetValue(name, out id))
  338. return indexCache[id];
  339. return null;
  340. }
  341. public AbstractViewGenerator GetViewGenerator(int id)
  342. {
  343. AbstractViewGenerator value;
  344. if (indexCache.TryGetValue(id, out value) == false)
  345. return null;
  346. return value;
  347. }
  348. public IndexCreationOptions FindIndexCreationOptions(IndexDefinition indexDef)
  349. {
  350. var indexDefinition = GetIndexDefinition(indexDef.Name);
  351. if (indexDefinition != null)
  352. {
  353. indexDef.IndexId = indexDefinition.IndexId;
  354. bool result = indexDefinition.Equals(indexDef);
  355. return result
  356. ? IndexCreationOptions.Noop
  357. : IndexCreationOptions.Update;
  358. }
  359. return IndexCreationOptions.Create;
  360. }
  361. public bool Contains(string indexName)
  362. {
  363. return indexNameToId.ContainsKey(indexName);
  364. }
  365. public static void ResolveAnalyzers(IndexDefinition indexDefinition)
  366. {
  367. // Stick Lucene.Net's namespace to all analyzer aliases that are missing a namespace
  368. var analyzerNames = (from analyzer in indexDefinition.Analyzers
  369. where analyzer.Value.IndexOf('.') == -1
  370. select analyzer).ToArray();
  371. // Only do this for analyzer that actually exist; we do this here to be able to throw a correct error later on
  372. foreach (
  373. var a in
  374. analyzerNames.Where(
  375. a => typeof(StandardAnalyzer).Assembly.GetType("Lucene.Net.Analysis." + a.Value) != null))
  376. {
  377. indexDefinition.Analyzers[a.Key] = "Lucene.Net.Analysis." + a.Value;
  378. }
  379. }
  380. public IDisposable TryRemoveIndexContext()
  381. {
  382. if (currentlyIndexingLock.TryEnterWriteLock(TimeSpan.FromSeconds(60)) == false)
  383. throw new InvalidOperationException(
  384. "Cannot modify indexes while indexing is in progress (already waited full minute). Try again later");
  385. return new DisposableAction(currentlyIndexingLock.ExitWriteLock);
  386. }
  387. public bool IsCurrentlyIndexing()
  388. {
  389. return Interlocked.Read(ref currentlyIndexing) != 0;
  390. }
  391. public IDisposable CurrentlyIndexing()
  392. {
  393. currentlyIndexingLock.EnterReadLock();
  394. Interlocked.Increment(ref currentlyIndexing);
  395. return new DisposableAction(() =>
  396. {
  397. currentlyIndexingLock.ExitReadLock();
  398. Interlocked.Decrement(ref currentlyIndexing);
  399. });
  400. }
  401. public void RemoveTransformer(string name)
  402. {
  403. var transformer = GetTransformerDefinition(name);
  404. if (transformer == null) return;
  405. RemoveTransformer(transformer.TransfomerId);
  406. }
  407. public void RemoveIndex(string name)
  408. {
  409. var index = GetIndexDefinition(name);
  410. if (index == null) return;
  411. RemoveIndex(index.IndexId);
  412. }
  413. public void RemoveTransformer(int id)
  414. {
  415. AbstractTransformer ignoredViewGenerator;
  416. int ignoredId;
  417. if (transformCache.TryRemove(id, out ignoredViewGenerator))
  418. transformNameToId.TryRemove(ignoredViewGenerator.Name, out ignoredId);
  419. TransformerDefinition ignoredIndexDefinition;
  420. transformDefinitions.TryRemove(id, out ignoredIndexDefinition);
  421. if (configuration.RunInMemory)
  422. return;
  423. File.Delete(GetIndexSourcePath(id) + ".transform");
  424. UpdateTransformerMappingFile();
  425. }
  426. public AbstractTransformer GetTransformer(int id)
  427. {
  428. AbstractTransformer value;
  429. if (transformCache.TryGetValue(id, out value) == false)
  430. return null;
  431. return value;
  432. }
  433. public AbstractTransformer GetTransformer(string name)
  434. {
  435. int id = 0;
  436. if (transformNameToId.TryGetValue(name, out id))
  437. return transformCache[id];
  438. return null;
  439. }
  440. private void UpdateIndexMappingFile()
  441. {
  442. if (configuration.RunInMemory)
  443. return;
  444. var sb = new StringBuilder();
  445. foreach (var index in indexNameToId)
  446. {
  447. sb.Append(string.Format("{0} - {1}{2}", index.Value, index.Key, Environment.NewLine));
  448. }
  449. File.WriteAllText(Path.Combine(path, "indexes.txt"), sb.ToString());
  450. }
  451. private void UpdateTransformerMappingFile()
  452. {
  453. if (configuration.RunInMemory)
  454. return;
  455. var sb = new StringBuilder();
  456. foreach (var transform in transformNameToId)
  457. {
  458. sb.Append(string.Format("{0} - {1}{2}", transform.Value, transform.Key, Environment.NewLine));
  459. }
  460. File.WriteAllText(Path.Combine(path, "transformers.txt"), sb.ToString());
  461. }
  462. }
  463. }