/// <summary> /// Directly enumerates the contents of BPlusTree from disk in read-only mode. /// </summary> /// <param name="options"> The options normally used to create the <see cref="BPlusTree{TKey, TValue}"/> instance </param> /// <returns> Yields the Key/Value pairs found in the file </returns> public static IEnumerable <KeyValuePair <TKey, TValue> > EnumerateFile(BPlusTreeOptions <TKey, TValue> options) { options = options.Clone(); options.CreateFile = CreatePolicy.Never; options.ReadOnly = true; using (INodeStorage store = options.CreateStorage()) { bool isnew; Node root; IStorageHandle hroot = store.OpenRoot(out isnew); if (isnew) { yield break; } NodeSerializer nodeReader = new NodeSerializer(options, new NodeHandleSerializer(store)); if (isnew || !store.TryGetNode(hroot, out root, nodeReader)) { throw new InvalidDataException(); } Stack <KeyValuePair <Node, int> > todo = new Stack <KeyValuePair <Node, int> >(); todo.Push(new KeyValuePair <Node, int>(root, 0)); while (todo.Count > 0) { KeyValuePair <Node, int> cur = todo.Pop(); if (cur.Value == cur.Key.Count) { continue; } todo.Push(new KeyValuePair <Node, int>(cur.Key, cur.Value + 1)); Node child; if (!store.TryGetNode(cur.Key[cur.Value].ChildNode.StoreHandle, out child, nodeReader)) { throw new InvalidDataException(); } if (child.IsLeaf) { for (int ix = 0; ix < child.Count; ix++) { var set = child[ix].ToKeyValuePair(); var enumerator = set.Value.GetEnumerator(); while (enumerator.MoveNext()) { yield return(new KeyValuePair <TKey, TValue>(set.Key, enumerator.Current.Key)); } } } else { todo.Push(new KeyValuePair <Node, int>(child, 0)); } } } }
static BPlusTreeOptions <TKey, TValue> Fix(BPlusTreeOptions <TKey, TValue> options) { if (options.LockingFactory.Create() is WriterOnlyLocking) { options.LockingFactory = new LockFactory <SimpleReadWriteLocking>(); } return(options); }
public NodeSerializer(BPlusTreeOptions <TKey, TValue> options, NodeHandleSerializer handleSerializer) { _options = options; _keySerializer = options.KeySerializer; _valueSerializer = options.ValueSerializer; _handleSerializer = handleSerializer; _storageHandleSerializer = handleSerializer; }
public NodeCacheBase(BPlusTreeOptions <TKey, TValue> options) { Options = options; LockFactory = Options.LockingFactory; Storage = Options.CreateStorage(); if (Options.UseStorageCache) { Storage = new StorageCache(Storage, Options.CacheKeepAliveMaximumHistory); } NodeSerializer = new NodeSerializer(Options, new NodeHandleSerializer(Storage)); _version = new NodeVersion(); }
/// <summary> /// Recovers as much file content as possible into a newly created <see cref="BPlusTree{TKey, TValue}"/>, if the operation returns /// a non-zero result it was successful and the file has been replaced with a new database containing /// the recovered data. The original file remains in-tact but was renamed with a '.deleted' extension. /// </summary> /// <remarks> /// If an exception occurs during the parsing of the file and one or more records were recovered, they will /// be stored in a file by the same name with an added extension of '.recovered'. This recovered file can be /// opened as a normal <see cref="BPlusTree{TKey, TValue}"/> to view it's contents. During the restore it is possible that /// a single Key was found multiple times, in this case the first occurrence found will be used. /// </remarks> /// <param name="options"> The options normally used to create the <see cref="BPlusTree{TKey, TValue}"/> instance </param> /// <returns>Returns 0 on failure, or the number of records successfully retrieved from the original file </returns> public static int RecoverFile(Options options) { int recoveredCount = 0; string filename = options.FileName; if (String.IsNullOrEmpty(filename)) { throw new InvalidConfigurationValueException("FileName", "The FileName property was not specified."); } if (!File.Exists(filename)) { throw new InvalidConfigurationValueException("FileName", "The FileName specified does not exist."); } if (options.StorageType != StorageType.Disk) { throw new InvalidConfigurationValueException("StorageType", "The storage type is not set to 'Disk'."); } int ix = 0; string tmpfilename = filename + ".recovered"; while (File.Exists(tmpfilename)) { tmpfilename = filename + ".recovered" + ix++; } try { BPlusTreeOptions <TKey, TValue> tmpoptions = options.Clone(); tmpoptions.CreateFile = CreatePolicy.Always; tmpoptions.FileName = tmpfilename; tmpoptions.LockingFactory = new LockFactory <IgnoreLocking>(); using (BPlusTree <TKey, TValue> tmpFile = new BPlusTree <TKey, TValue>(tmpoptions)) { BulkInsertOptions bulkOptions = new BulkInsertOptions(); bulkOptions.DuplicateHandling = DuplicateHandling.LastValueWins; recoveredCount = tmpFile.BulkInsert(RecoveryScan(options, FileShare.None), bulkOptions); } } finally { if (recoveredCount == 0 && File.Exists(tmpfilename)) { File.Delete(tmpfilename); } } if (recoveredCount > 0) { ix = 0; string backupName = filename + ".deleted"; while (File.Exists(backupName)) { backupName = filename + ".deleted" + ix++; } File.Move(filename, backupName); try { File.Move(tmpfilename, filename); } catch { File.Move(backupName, filename); throw; } } return(recoveredCount); }
public NodeCacheNormal(BPlusTreeOptions <TKey, TValue> options) : base(options) { _keepAlive = options.CreateCacheKeepAlive(); _cache = new Dictionary <NodeHandle, Utils.WeakReference <CacheEntry> >(); _cacheLock = new ReaderWriterLocking(); }
static BPlusTreeOptions <TKey, TValue> Fix(BPlusTreeOptions <TKey, TValue> options) { return(options); }
public NodeCacheNone(BPlusTreeOptions <TKey, TValue> options) : base(Fix(options)) { _lock = new ReaderWriterLocking(); _list = new Dictionary <IStorageHandle, ILockStrategy>(); }
public BPlusTree(BPlusTreeOptions <TKey, TValue> ioptions) { bool fileExists = ioptions.StorageType == StorageType.Disk && ioptions.CreateFile != CreatePolicy.Always && File.Exists(ioptions.FileName) && new FileInfo(ioptions.FileName).Length > 0; _options = ioptions.Clone(); _selfLock = _options.CallLevelLock; _keyComparer = _options.KeyComparer; _itemComparer = new ElementComparer(_keyComparer); switch (_options.CachePolicy) { case CachePolicy.All: _storage = new NodeCacheFull(_options); break; case CachePolicy.Recent: _storage = new NodeCacheNormal(_options); break; case CachePolicy.None: _storage = new NodeCacheNone(_options); break; default: throw new InvalidConfigurationValueException("CachePolicy"); } try { _storage.Load(); } catch { _storage.Dispose(); throw; } if (_options.LogFile != null && !_options.ReadOnly) { if (_options.ExistingLogAction == ExistingLogAction.Truncate || _options.ExistingLogAction == ExistingLogAction.Default && !fileExists) { _options.LogFile.TruncateLog(); } else if (_options.LogFile.Size > 0 && ( _options.ExistingLogAction == ExistingLogAction.Replay || _options.ExistingLogAction == ExistingLogAction.ReplayAndCommit || (_options.ExistingLogAction == ExistingLogAction.Default && fileExists) )) { bool commit = (_options.ExistingLogAction == ExistingLogAction.ReplayAndCommit || (_options.ExistingLogAction == ExistingLogAction.Default && fileExists)); bool merge = false; if (_options.StorageType == StorageType.Disk) { merge = new FileInfo(_options.FileName).Length < _options.LogFile.Size; } if (merge) // log data is larger than we are... { BulkInsertOptions opts = new BulkInsertOptions(); opts.CommitOnCompletion = commit; opts.DuplicateHandling = DuplicateHandling.LastValueWins; opts.InputIsSorted = true; opts.ReplaceContents = true; BulkInsert( _options.LogFile.MergeLog(_options.KeyComparer, File.Exists(ioptions.FileName) ? EnumerateFile(ioptions) : new KeyValuePair <TKey, TValue> [0]), opts ); } else { _options.LogFile.ReplayLog(this); if (commit) //Now commit the recovered changes { Commit(); } } } } var nodeStoreWithCount = _storage.Storage as INodeStoreWithCount; if (nodeStoreWithCount != null) { _count = nodeStoreWithCount.Count; _hasCount = _count >= 0; } }
public NodeCacheFull(BPlusTreeOptions <TKey, TValue> options) : base(options) { }
public NodeCacheNormal(BPlusTreeOptions <TKey, TValue> options) : base(options) { _keepAlive = new ObjectKeepAlive(options.CacheKeepAliveMinimumHistory, options.CacheKeepAliveMaximumHistory, TimeSpan.FromMilliseconds(options.CacheKeepAliveTimeout)); _cache = new Dictionary <NodeHandle, WeakReference <CacheEntry> >(); _cacheLock = new SimpleReadWriteLocking(); }