Database.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Threading;
  6. using Best.HTTP.Shared.Extensions;
  7. using Best.HTTP.Shared.PlatformSupport.Memory;
  8. using Best.HTTP.Shared.PlatformSupport.Threading;
  9. namespace Best.HTTP.Shared.Databases
  10. {
  11. public sealed class FolderAndFileOptions
  12. {
  13. public string FolderName = "Best.HTTP.Shared.Databases";
  14. public string DatabaseFolderName = "Databases";
  15. public string MetadataExtension = "metadata";
  16. public string DatabaseExtension = "db";
  17. public string DatabaseFreeListExtension = "freelist";
  18. public string HashExtension = "hash";
  19. }
  20. public abstract class Database<ContentType, MetadataType, IndexingServiceType, MetadataServiceType> : IDisposable, IHeartbeat
  21. where MetadataType : Metadata, new()
  22. where IndexingServiceType : IndexingService<ContentType, MetadataType>
  23. where MetadataServiceType : MetadataService<MetadataType, ContentType>
  24. {
  25. public static FolderAndFileOptions FolderAndFileOptions = new FolderAndFileOptions();
  26. public string SaveDir { get; private set; }
  27. public string Name { get { return this.Options.Name; } }
  28. public string MetadataFileName { get { return Path.ChangeExtension(Path.Combine(this.SaveDir, this.Name), FolderAndFileOptions.MetadataExtension); } }
  29. public string DatabaseFileName { get { return Path.ChangeExtension(Path.Combine(this.SaveDir, this.Name), FolderAndFileOptions.DatabaseExtension); } }
  30. public string DatabaseFreeListFileName { get { return Path.ChangeExtension(Path.Combine(this.SaveDir, this.Name), FolderAndFileOptions.DatabaseFreeListExtension); } }
  31. public string HashFileName { get { return Path.ChangeExtension(Path.Combine(this.SaveDir, this.Name), FolderAndFileOptions.HashExtension); } }
  32. public MetadataServiceType MetadataService { get; private set; }
  33. protected DatabaseOptions Options { get; private set; }
  34. protected IndexingServiceType IndexingService { get; private set; }
  35. protected DiskManager<ContentType> DiskManager { get; private set; }
  36. protected int isDirty = 0;
  37. protected ReaderWriterLockSlim rwlock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
  38. public Database(string directory,
  39. DatabaseOptions options,
  40. IndexingServiceType indexingService,
  41. IDiskContentParser<ContentType> diskContentParser,
  42. MetadataServiceType metadataService)
  43. {
  44. this.SaveDir = directory;
  45. this.Options = options;
  46. this.IndexingService = indexingService;
  47. this.MetadataService = metadataService;
  48. var dir = Path.GetDirectoryName(this.DatabaseFileName);
  49. if (!HTTPManager.IOService.DirectoryExists(dir))
  50. HTTPManager.IOService.DirectoryCreate(dir);
  51. this.DiskManager = new DiskManager<ContentType>(
  52. HTTPManager.IOService.CreateFileStream(this.DatabaseFileName, Best.HTTP.Shared.PlatformSupport.FileSystem.FileStreamModes.OpenReadWrite),
  53. HTTPManager.IOService.CreateFileStream(this.DatabaseFreeListFileName, Best.HTTP.Shared.PlatformSupport.FileSystem.FileStreamModes.OpenReadWrite),
  54. diskContentParser,
  55. options.DiskManager);
  56. using (var fileStream = HTTPManager.IOService.CreateFileStream(this.MetadataFileName, Best.HTTP.Shared.PlatformSupport.FileSystem.FileStreamModes.OpenReadWrite))
  57. using (var stream = new BufferedStream(fileStream))
  58. this.MetadataService.LoadFrom(stream);
  59. }
  60. public int Clear()
  61. {
  62. using (new WriteLock(this.rwlock))
  63. {
  64. int count = this.MetadataService.Metadatas.Count;
  65. this.IndexingService.Clear();
  66. this.DiskManager.Clear();
  67. this.MetadataService.Clear();
  68. FlagDirty(1);
  69. return count;
  70. }
  71. }
  72. public int Delete(IEnumerable<MetadataType> metadatas)
  73. {
  74. if (metadatas == null)
  75. return 0;
  76. using (new WriteLock(this.rwlock))
  77. {
  78. int deletedCount = 0;
  79. foreach (var metadata in metadatas)
  80. if (DeleteMetadata(metadata))
  81. deletedCount++;
  82. FlagDirty(deletedCount);
  83. return deletedCount;
  84. }
  85. }
  86. public int Delete(IEnumerable<int> metadataIndexes)
  87. {
  88. if (metadataIndexes == null)
  89. return 0;
  90. using (new WriteLock(this.rwlock))
  91. {
  92. int deletedCount = 0;
  93. foreach (int idx in metadataIndexes)
  94. {
  95. var metadata = this.MetadataService.Metadatas[idx];
  96. if (DeleteMetadata(metadata))
  97. deletedCount++;
  98. }
  99. FlagDirty(deletedCount);
  100. return deletedCount;
  101. }
  102. }
  103. protected bool DeleteMetadata(MetadataType metadata)
  104. {
  105. if (metadata.Length > 0)
  106. this.DiskManager.Delete(metadata);
  107. this.MetadataService.Remove(metadata);
  108. FlagDirty(1);
  109. return true;
  110. }
  111. /// <summary>
  112. /// Loads the first content from the metadata indexes.
  113. /// </summary>
  114. public ContentType FromFirstMetadataIndex(IEnumerable<int> metadataIndexes)
  115. {
  116. if (metadataIndexes == null)
  117. return default;
  118. var index = metadataIndexes.DefaultIfEmpty(-1).First();
  119. if (index < 0)
  120. return default;
  121. return FromMetadataIndex(index);
  122. }
  123. /// <summary>
  124. /// Loads the content from the metadata index.
  125. /// </summary>
  126. public ContentType FromMetadataIndex(int metadataIndex)
  127. {
  128. if (metadataIndex < 0 || metadataIndex >= this.MetadataService.Metadatas.Count)
  129. return default;
  130. //using (new ReadLock(this.rwlock))
  131. {
  132. var metadata = this.MetadataService.Metadatas[metadataIndex];
  133. return this.DiskManager.Load(metadata);
  134. }
  135. }
  136. public ContentType FromMetadata(MetadataType metadata) => this.DiskManager.Load(metadata);
  137. /// <summary>
  138. /// Loads all content from the metadatas.
  139. /// </summary>
  140. public IEnumerable<ContentType> FromMetadatas(IEnumerable<MetadataType> metadatas) => FromMetadataIndexes(from m in metadatas select m.Index);
  141. /// <summary>
  142. /// Loads all content from the metadata indexes.
  143. /// </summary>
  144. public IEnumerable<ContentType> FromMetadataIndexes(IEnumerable<int> metadataIndexes)
  145. {
  146. if (metadataIndexes == null)
  147. yield break;
  148. //using (new ReadLock(this.rwlock))
  149. {
  150. foreach (int metadataIndex in metadataIndexes)
  151. {
  152. var metadata = this.MetadataService.Metadatas[metadataIndex];
  153. var content = this.DiskManager.Load(metadata);
  154. //result.Add(content);
  155. yield return content;
  156. }
  157. }
  158. }
  159. protected void FlagDirty(int dirty)
  160. {
  161. if (dirty != 0 && Interlocked.CompareExchange(ref this.isDirty, dirty, 0) == 0)
  162. HTTPManager.Heartbeats.Subscribe(this);
  163. }
  164. public bool Save()
  165. {
  166. if (!this.rwlock.TryEnterWriteLock(TimeSpan.FromMilliseconds(0)))
  167. return false;
  168. try
  169. {
  170. int itWasDirty = Interlocked.CompareExchange(ref this.isDirty, 0, 1);
  171. if (itWasDirty == 0)
  172. return true;
  173. using (var fileStream = HTTPManager.IOService.CreateFileStream(this.MetadataFileName, Best.HTTP.Shared.PlatformSupport.FileSystem.FileStreamModes.Create))
  174. using (var stream = new BufferedStream(fileStream))
  175. this.MetadataService.SaveTo(stream);
  176. if (this.Options.UseHashFile)
  177. {
  178. using (var hashStream = HTTPManager.IOService.CreateFileStream(this.HashFileName, Best.HTTP.Shared.PlatformSupport.FileSystem.FileStreamModes.Create))
  179. {
  180. var hash = this.DiskManager.CalculateHash();
  181. hashStream.Write(hash.Data, 0, hash.Count);
  182. BufferPool.Release(hash);
  183. }
  184. }
  185. this.DiskManager.Save();
  186. Interlocked.Exchange(ref this.isDirty, 0);
  187. return true;
  188. }
  189. finally
  190. {
  191. this.rwlock.ExitWriteLock();
  192. }
  193. }
  194. void IHeartbeat.OnHeartbeatUpdate(DateTime now, TimeSpan dif)
  195. {
  196. if (this.Save())
  197. HTTPManager.Heartbeats.Unsubscribe(this);
  198. }
  199. public void Dispose()
  200. {
  201. Save();
  202. this.DiskManager.Dispose();
  203. this.rwlock.Dispose();
  204. GC.SuppressFinalize(this);
  205. }
  206. }
  207. }