public void TestReadOnly()
        {
            using (TempFile file = new TempFile())
            {
                var opt = new BPlusTree<int, int>.Options(PrimitiveSerializer.Int32, PrimitiveSerializer.Int32)
                {
                    CreateFile = CreatePolicy.Always,
                    FileName = file.TempPath,
                };
                using (BPlusTree<int, int> tree = new BPlusTree<int, int>(opt))
                {
                    tree.Add(1, 2);
                    tree.Add(3, 4);
                    tree.Add(5, 6);
                }

                opt.CreateFile = CreatePolicy.Never;
                opt.ReadOnly = true;
                using (BPlusTree<int, int> tree = new BPlusTree<int, int>(opt))
                {
                    Assert.AreEqual(tree[1], 2);
                    Assert.AreEqual(tree[3], 4);
                    Assert.AreEqual(tree[5], 6);

                    try { tree[1] = 0; Assert.Fail(); }
                    catch (InvalidOperationException) { }

                    try { tree.Remove(1); Assert.Fail(); }
                    catch (InvalidOperationException) { }
                }
            }
        }
 public void BPlusTreeDemo()
 {
     var options = new BPlusTree<string, DateTime>.OptionsV2(PrimitiveSerializer.String, PrimitiveSerializer.DateTime);
     options.CalcBTreeOrder(16, 24);
     options.CreateFile = CreatePolicy.Always;
     options.FileName = Path.GetTempFileName();
     using (var tree = new BPlusTree<string, DateTime>(options))
     {
         var tempDir = new DirectoryInfo(Path.GetTempPath());
         foreach (var file in tempDir.GetFiles("*", SearchOption.AllDirectories))
         {
             tree.Add(file.FullName, file.LastWriteTimeUtc);
         }
     }
     options.CreateFile = CreatePolicy.Never;
     using (var tree = new BPlusTree<string, DateTime>(options))
     {
         var tempDir = new DirectoryInfo(Path.GetTempPath());
         foreach (var file in tempDir.GetFiles("*", SearchOption.AllDirectories))
         {
             DateTime cmpDate;
             if (!tree.TryGetValue(file.FullName, out cmpDate))
                 Console.WriteLine("New file: {0}", file.FullName);
             else if (cmpDate != file.LastWriteTimeUtc)
                 Console.WriteLine("Modified: {0}", file.FullName);
             tree.Remove(file.FullName);
         }
         foreach (var item in tree)
         {
             Console.WriteLine("Removed: {0}", item.Key);
         }
     }
 }
 static void Insert(BPlusTree<Guid, TestInfo> tree, IDictionary<Guid, TestInfo> testdata, int threads, int count, TimeSpan wait)
 {
     using (var work = new WorkQueue<IEnumerable<KeyValuePair<Guid, TestInfo>>>(tree.AddRange, threads))
     {
         foreach (var set in TestInfo.CreateSets(threads, count, testdata))
             work.Enqueue(set);
         work.Complete(false, wait == TimeSpan.MaxValue ? Timeout.Infinite : (int)Math.Min(int.MaxValue, wait.TotalMilliseconds));
     }
 }
Example #4
0
        // key, data, added, last_used
        /// <summary>
        /// Initialize the cache.
        /// </summary>
        /// <param name="capacity">Maximum number of entries.</param>
        /// <param name="evictCount">Number to evict when capacity is reached.</param>
        /// <param name="debug">Enable or disable console debugging.</param>
        public LRUCacheBTree(int capacity, int evictCount, bool debug)
        {
            Capacity = capacity;
            EvictCount = evictCount;
            Debug = debug;
            Cache = new BPlusTree<string, Tuple<object, DateTime, DateTime>>();
            Cache.EnableCount();

            if (EvictCount > Capacity)
            {
                throw new ArgumentException("Evict count must be less than or equal to capacity.");
            }
        }
        BPlusTree<Guid, TestInfo>.OptionsV2 GetOptions(TempFile temp)
        {
            BPlusTree<Guid, TestInfo>.OptionsV2 options = new BPlusTree<Guid, TestInfo>.OptionsV2(
                PrimitiveSerializer.Guid, new TestInfoSerializer());
            options.CalcBTreeOrder(Marshal.SizeOf(typeof(Guid)), Marshal.SizeOf(typeof(TestInfo)));
            options.CreateFile = CreatePolicy.IfNeeded;
            options.FileName = temp.TempPath;

            // The following three options allow for automatic commit/recovery:
            options.CallLevelLock = new ReaderWriterLocking();
            options.TransactionLogFileName = Path.ChangeExtension(options.FileName, ".tlog");
            return options;
        }
 public void TestICloneable()
 {
     ICloneable opt = new BPlusTree<int, int>.Options(PrimitiveSerializer.Int32, PrimitiveSerializer.Int32) 
     {
         CreateFile = CreatePolicy.IfNeeded,
         BTreeOrder = 4
     };
     BPlusTree<int, int>.Options options = (BPlusTree<int, int>.Options)opt.Clone();
     
     Assert.AreEqual(CreatePolicy.IfNeeded, options.CreateFile);
     Assert.AreEqual(4, options.MaximumChildNodes);
     Assert.AreEqual(4, options.MaximumValueNodes);
 }
        public void TestReadOnlyCopy()
        {
            using (var tempFile = new TempFile())
            {
                var options = new BPlusTree<int, string>.OptionsV2(new PrimitiveSerializer(), new PrimitiveSerializer())
                                  {
                                      CreateFile = CreatePolicy.Always,
                                      FileName = tempFile.TempPath,
                                  }.CalcBTreeOrder(4, 10);

                var readcopy = options.Clone();
                readcopy.CreateFile = CreatePolicy.Never;
                readcopy.ReadOnly = true;

                using (var tree = new BPlusTree<int, string>(options))
                {
                    using (var copy = new BPlusTree<int, string>(readcopy))
                    {
                        copy.EnableCount();
                        Assert.AreEqual(0, copy.Count);
                    }

                    //insert some data...
                    tree.AddRange(MakeValues(0, 100));

                    using (var copy = new BPlusTree<int, string>(readcopy))
                    {
                        copy.EnableCount();
                        Assert.AreEqual(0, copy.Count);
                    }
                    tree.Commit();

                    //insert some data...
                    for (int i = 0; i < 100; i++)
                        tree.Remove(i);
                    tree.AddRange(MakeValues(1000, 1000));

                    using (var copy = new BPlusTree<int, string>(readcopy))
                    {
                        copy.EnableCount();
                        Assert.AreEqual(100, copy.Count);
                        Assert.AreEqual(0, copy.First().Key);
                        Assert.AreEqual(99, copy.Last().Key);
                    }

                    tree.Commit();

                }
            }
        }
        public void TestCloneWithCallLockV1()
        {
            var options = new BPlusTree<int, int>.Options(PrimitiveSerializer.Int32, PrimitiveSerializer.Int32)
            {
                CreateFile = CreatePolicy.IfNeeded,
                BTreeOrder = 4
            };
            var copy = options.Clone();

            Assert.IsFalse(Object.ReferenceEquals(options, copy));
            Assert.IsTrue(Object.ReferenceEquals(options.CallLevelLock, copy.CallLevelLock));

            //If we get/set the lock prior to clone we will have the same lock instance.
            options.CallLevelLock = new SimpleReadWriteLocking();
            copy = options.Clone();

            Assert.IsFalse(Object.ReferenceEquals(options, copy));
            Assert.IsTrue(Object.ReferenceEquals(options.CallLevelLock, copy.CallLevelLock));
        }
        public void TestConcurrency()
        {
            mreStop.Reset();
            using(TempFile temp = new TempFile())
            {
                BPlusTree<Guid, TestInfo>.OptionsV2 options = new BPlusTree<Guid, TestInfo>.OptionsV2(
                    PrimitiveSerializer.Guid, new TestInfoSerializer());
                options.CalcBTreeOrder(16, 24);
                options.CreateFile = CreatePolicy.Always;
                options.FileName = temp.TempPath;
                using (BPlusTree<Guid, TestInfo> tree = new BPlusTree<Guid, TestInfo>(options))
                {
                    tree.EnableCount();
                    var actions = new List<IAsyncResult>();
                    var tests = new Action<BPlusTree<Guid, TestInfo>>[] 
                    {
                        DeleteStuff, UpdateStuff, AddStuff, AddRanges, BulkyInserts,
                        FetchStuff, FetchStuff, FetchStuff, FetchStuff, FetchStuff
                    };

                    foreach (var t in tests)
                        actions.Add(t.BeginInvoke(tree, null, null));

					const int waitIterations = 8;	// wait for n seconds

					int timesWaited = 0;
                    do
                    {
                        Trace.TraceInformation("Dictionary.Count = {0}", tree.Count);
                        Thread.Sleep(1000);
						timesWaited++;
                    } while (timesWaited<waitIterations && Debugger.IsAttached);

                    mreStop.Set();
                    for (int i = 0; i < actions.Count; i++)
                    {
                        tests[i].EndInvoke(actions[i]);
                    }

                    Trace.TraceInformation("Dictionary.Count = {0}", tree.Count);
                }
            }
        }
 void DeleteStuff(BPlusTree<Guid, TestInfo> tree)
 {
     while (!mreStop.WaitOne(0, false))
     {
         if (tree.Count > 1000)
         {
             int limit = tree.Count - 1000;
             foreach (Guid key in tree.Keys)
             {
                 if (!tree.Remove(key))
                     throw new ApplicationException();
                 if (--limit <= 0)
                     break;
             }
         }
         else
             Thread.Sleep(1);
     }
 }
        public void TestWriteToTemporaryCopy()
        {
            Dictionary<Guid, TestInfo> first, data = new Dictionary<Guid, TestInfo>();
            using(TempFile temp = new TempFile())
            {
                temp.Delete();
                var options = GetOptions(temp);
                options.TransactionLogFileName = Path.ChangeExtension(options.FileName, ".tlog");

                using (var tree = new BPlusTree<Guid, TestInfo>(options))
                {
                    Insert(tree, data, 1, 100, TimeSpan.MaxValue);
                    TestInfo.AssertEquals(data, tree);
                    Assert.IsFalse(temp.Exists);
                }

                // All data commits to output file
                Assert.IsTrue(temp.Exists);
                TestInfo.AssertEquals(data, BPlusTree<Guid, TestInfo>.EnumerateFile(options));

                first = new Dictionary<Guid, TestInfo>(data);
                
                using (var tree = new BPlusTree<Guid, TestInfo>(options))
                {
                    Insert(tree, data, 1, 100, TimeSpan.MaxValue);

                    //We are writing to a backup, the original file still contains 100 items:
                    TestInfo.AssertEquals(first, BPlusTree<Guid, TestInfo>.EnumerateFile(options));

                    //Commit the changes and the original file will now contain our changes:
                    tree.CommitChanges();
                    TestInfo.AssertEquals(data, BPlusTree<Guid, TestInfo>.EnumerateFile(options));

                    //Add a few more records...
                    Insert(tree, data, 1, 100, TimeSpan.MaxValue);
                }
                //Dispose of the tree will commit changes...
                TestInfo.AssertEquals(data, BPlusTree<Guid, TestInfo>.EnumerateFile(options));
            }
        }
Example #12
0
 /// <summary>
 /// Clear the cache.
 /// </summary>
 public void Clear()
 {
     lock (CacheLock)
     {
         Cache = new BPlusTree<string, Tuple<object, DateTime, DateTime>>();
         Cache.EnableCount();
         return;
     }
 }
        public void TestRestoreLargeLog()
        {
            using (TempFile savelog = new TempFile())
            using (TempFile temp = new TempFile())
            {
                var options = GetOptions(temp);
                options.FileBlockSize = 512;
                options.StoragePerformance = StoragePerformance.Fastest;
                options.CalcBTreeOrder(Marshal.SizeOf(typeof(Guid)), Marshal.SizeOf(typeof(TestInfo)));
                options.TransactionLog = new TransactionLog<Guid, TestInfo>(
                    new TransactionLogOptions<Guid, TestInfo>(
                        options.TransactionLogFileName,
                        options.KeySerializer,
                        options.ValueSerializer
                        )
                    );

                //Now recover...
                Dictionary<Guid, TestInfo> first = new Dictionary<Guid, TestInfo>();
                Dictionary<Guid, TestInfo> sample;

                using (var tree = new BPlusTree<Guid, TestInfo>(options))
                {
                    tree.EnableCount();
                    Insert(tree, first, 1, 100, TimeSpan.FromMinutes(1));
                    tree.Commit();

                    Assert.AreEqual(100, tree.Count);

                    sample = new Dictionary<Guid, TestInfo>(first);
                    Insert(tree, sample, 7, 5000, TimeSpan.FromMinutes(1));

                    Assert.AreEqual(35100, tree.Count);

                    for (int i = 0; i < 1; i++)
                    {
                        foreach (var rec in tree)
                        {
                            var value = rec.Value;
                            value.UpdateCount++;
                            value.ReadCount++;
                            tree[rec.Key] = value;
                        }
                    }
                    
                    File.Copy(options.TransactionLog.FileName, savelog.TempPath, true);
                    tree.Rollback();

                    TestInfo.AssertEquals(first, tree);
                }

                //file still has initial committed data
                TestInfo.AssertEquals(first, BPlusTree<Guid, TestInfo>.EnumerateFile(options));

                //restore the log and verify all data.
                File.Copy(savelog.TempPath, options.TransactionLog.FileName, true);
                using (var tree = new BPlusTree<Guid, TestInfo>(options))
                {
                    TestInfo.AssertEquals(sample, tree);
                }

                //file still has initial committed data
                TestInfo.AssertEquals(sample, BPlusTree<Guid, TestInfo>.EnumerateFile(options));
            }
        }
        void TestRecoveryOnNew(BPlusTree<Guid, TestInfo>.OptionsV2 options, int count, int added)
        {
            BPlusTree<Guid, TestInfo> tree = null;
            var temp = TempFile.Attach(options.FileName);
            Dictionary<Guid, TestInfo> data = new Dictionary<Guid, TestInfo>();
            try
            {
                Assert.IsNotNull(options.TransactionLog);
                temp.Delete();
                tree = new BPlusTree<Guid, TestInfo>(options);
                using (var log = options.TransactionLog)
                {
                    using ((IDisposable)new PropertyValue(tree, "_storage").Value)
                        Insert(tree, data, Environment.ProcessorCount, count, TimeSpan.MaxValue);
                    //Add extra data...
                    AppendToLog(log, TestInfo.Create(added, data));
                }
                tree = null;
                //No file... yet...
                Assert.IsFalse(File.Exists(options.FileName));
                //Now recover...
                using (var recovered = new BPlusTree<Guid, TestInfo>(options))
                {
                    TestInfo.AssertEquals(data, recovered);
                }

                Assert.IsTrue(File.Exists(options.FileName));
            }
            finally
            {
                temp.Dispose();
                if (tree != null)
                    tree.Dispose();
            }
        }
        void TestRecoveryOnExisting(BPlusTree<Guid, TestInfo>.OptionsV2 options, int count, int added)
        {
            BPlusTree<Guid, TestInfo> tree = null;
            var temp = TempFile.Attach(options.FileName);
            Dictionary<Guid, TestInfo> dataFirst, data = new Dictionary<Guid, TestInfo>();
            try
            {
                temp.Delete();
                Assert.IsNotNull(options.TransactionLog);

                using (tree = new BPlusTree<Guid, TestInfo>(options))
                {
                    Insert(tree, data, 1, 100, TimeSpan.MaxValue);
                    TestInfo.AssertEquals(data, tree);
                    Assert.IsFalse(temp.Exists);
                }
                tree = null;
                Assert.IsTrue(File.Exists(options.TransactionLogFileName));

                // All data commits to output file
                Assert.IsTrue(temp.Exists);
                TestInfo.AssertEquals(data, BPlusTree<Guid, TestInfo>.EnumerateFile(options));

                dataFirst = new Dictionary<Guid, TestInfo>(data);
                DateTime modified = temp.Info.LastWriteTimeUtc;

                tree = new BPlusTree<Guid, TestInfo>(options);
                using (var log = options.TransactionLog)
                {
                    using ((IDisposable) new PropertyValue(tree, "_storage").Value)
                        Insert(tree, data, Environment.ProcessorCount, count, TimeSpan.MaxValue);
                    //Add extra data...
                    AppendToLog(log, TestInfo.Create(added, data));
                }
                tree = null;

                //Still only contains original data
                Assert.AreEqual(modified, temp.Info.LastWriteTimeUtc);
                TestInfo.AssertEquals(dataFirst, BPlusTree<Guid, TestInfo>.EnumerateFile(options));

                //Now recover...
                using (var recovered = new BPlusTree<Guid, TestInfo>(options))
                {
                    TestInfo.AssertEquals(data, recovered);
                }
            }
            finally
            {
                temp.Dispose();
                if (tree != null)
                    tree.Dispose();
            }
        }
        public void TestBulkInsertSorted()
        {
            Dictionary<int, string> test = new Dictionary<int, string>();
            IEnumerable<KeyValuePair<int, string>>[] sets = 
                new List<IEnumerable<KeyValuePair<int, string>>>(CreateSets(1, 1000, test)).ToArray();

            using (BPlusTree<int, string> tree = new BPlusTree<int, string>(Options))
            {
                tree.BulkInsert(
                    new OrderedKeyValuePairs<int, string>(sets[0]),
                    new BulkInsertOptions { DuplicateHandling = DuplicateHandling.LastValueWins, InputIsSorted = true }
                    );

                VerifyDictionary(test, tree);
            }
        }
 void FetchStuff(BPlusTree<Guid, TestInfo> tree)
 {
     while (!mreStop.WaitOne(0, false))
     {
         foreach (Guid k in tree.Keys)
         {
             TestInfo ti;
             if (tree.TryGetValue(k, out ti) && ti.MyKey != k)
                 throw new ApplicationException();
         }
     }
 }
 void BulkyInserts(BPlusTree<Guid, TestInfo> tree)
 {
     while (!mreStop.WaitOne(0, false))
     {
         tree.BulkInsert(CreateData(100));
         AddIdle(tree);
         Thread.Sleep(100);
     }
 }
        public void TestSyncFromLogging()
        {
            using (var tempFile = new TempFile())
            using (var logfile = new TempFile())
            using (var tempCopy = new TempFile())
            {
                var options = new BPlusTree<int, string>.OptionsV2(new PrimitiveSerializer(), new PrimitiveSerializer())
                {
                    CreateFile = CreatePolicy.Always,
                    FileName = tempFile.TempPath,
                    TransactionLogFileName = logfile.TempPath,
                }.CalcBTreeOrder(4, 10);

                var readcopy = options.Clone();
                readcopy.FileName = tempCopy.TempPath;
                readcopy.StoragePerformance = StoragePerformance.Fastest;

                using (var tree = new BPlusTree<int, string>(options))
                using (var copy = new BPlusTree<int, string>(readcopy))
                using (var tlog = new TransactionLog<int, string>(
                    new TransactionLogOptions<int, string>(logfile.TempPath, PrimitiveSerializer.Int32, PrimitiveSerializer.String) { ReadOnly = true }))
                {
                    tree.Add(0, "0");
                    tree.Commit();

                    long logpos = 0;
                    copy.EnableCount();
                    //start by copying the data from tree's file into the copy instance:
                    copy.BulkInsert(
                        BPlusTree<int, string>.EnumerateFile(options),
                        new BulkInsertOptions { InputIsSorted = true, CommitOnCompletion = false, ReplaceContents = true }
                        );

                    Assert.AreEqual(1, copy.Count);
                    Assert.AreEqual("0", copy[0]);

                    tlog.ReplayLog(copy, ref logpos);
                    Assert.AreEqual(1, copy.Count);

                    //insert some data...
                    tree.AddRange(MakeValues(1, 99));

                    tlog.ReplayLog(copy, ref logpos);
                    Assert.AreEqual(100, copy.Count);

                    //insert some data...
                    for (int i = 0; i < 100; i++)
                        tree.Remove(i);
                    tlog.ReplayLog(copy, ref logpos);
                    Assert.AreEqual(0, copy.Count);

                    tree.AddRange(MakeValues(1000, 1000));

                    tlog.ReplayLog(copy, ref logpos);
                    Assert.AreEqual(1000, copy.Count);
                }
            }
        }
        void TestMergeSequenceInFile(BPlusTreeOptions<int, string> options, int count)
        {
            Dictionary<int, string> expected = new Dictionary<int, string>();

            for (int i = 0; i < count; i++)
                expected.Add(i + 1, i.ToString());

            using (BPlusTree<int, string> tree = new BPlusTree<int, string>(options))
            {
                Assert.AreEqual(expected.Count, tree.BulkInsert(expected));
                VerifyDictionary(expected, tree);
            }
        }
 void AddRanges(BPlusTree<Guid, TestInfo> tree)
 {
     while (!mreStop.WaitOne(0, false))
     {
         tree.AddRange(CreateData(100));
         AddIdle(tree);
     }
 }
 private void AddIdle(BPlusTree<Guid, TestInfo> tree)
 {
     int size = tree.Count;
     if (size > 100000)
         Thread.Sleep(size - 100000);
 }
 void UpdateStuff(BPlusTree<Guid, TestInfo> tree)
 {
     while (!mreStop.WaitOne(0, false))
     {
         foreach (var pair in tree)
         {
             bool updated = tree.TryUpdate(pair.Key, (k, v) => { v.UpdateCount++; return v; });
             if (!updated && tree.ContainsKey(pair.Key))
                 throw new ApplicationException();
         }
     }
 }
        public void TestReplaceContents()
        {
            Dictionary<int, string> test = new Dictionary<int, string>();
            IEnumerable<KeyValuePair<int, string>>[] sets =
                new List<IEnumerable<KeyValuePair<int, string>>>(CreateSets(1, 1000, test)).ToArray();

            using (BPlusTree<int, string> tree = new BPlusTree<int, string>(Options))
            {
                tree.BulkInsert(
                    new OrderedKeyValuePairs<int, string>(sets[0]),
                    new BulkInsertOptions { DuplicateHandling = DuplicateHandling.LastValueWins, InputIsSorted = true }
                    );

                VerifyDictionary(test, tree);

                // Use bulk insert to overwrite the contents of tree
                test = new Dictionary<int, string>();
                sets = new List<IEnumerable<KeyValuePair<int, string>>>(CreateSets(1, 100, test)).ToArray();

                tree.BulkInsert(
                    new OrderedKeyValuePairs<int, string>(sets[0]),
                    new BulkInsertOptions 
                    {
                        CommitOnCompletion = false,
                        InputIsSorted = true,
                        ReplaceContents = true,
                        DuplicateHandling = DuplicateHandling.RaisesException, 
                    }
                );

                VerifyDictionary(test, tree);
            }
        }
        void SequencedTest(int start, int incr, int stop, string name)
        {
            int count = Math.Abs(start - stop)/Math.Abs(incr);
            const string myTestValue1 = "T1", myTestValue2 = "t2";
            string test;

            using (BPlusTree<int, string> data = new BPlusTree<int, string>(Options))
            {
                Stopwatch time = new Stopwatch();
                time.Start();
                //large order-forward
                for (int i = start; i != stop; i += incr)
                    if (!data.TryAdd(i, myTestValue1)) throw new ApplicationException();

                Trace.TraceInformation("{0} insert  {1} in {2}", name, count, time.ElapsedMilliseconds);
                time.Reset();
                time.Start();

                for (int i = start; i != stop; i += incr)
                    if (!data.TryGetValue(i, out test) || test != myTestValue1) throw new ApplicationException();

                Trace.TraceInformation("{0} seek    {1} in {2}", name, count, time.ElapsedMilliseconds);
                time.Reset();
                time.Start();

                for (int i = start; i != stop; i += incr)
                    if (!data.TryUpdate(i, myTestValue2)) throw new ApplicationException();

                Trace.TraceInformation("{0} modify  {1} in {2}", name, count, time.ElapsedMilliseconds);
                time.Reset();
                time.Start();

                for (int i = start; i != stop; i += incr)
                    if (!data.TryGetValue(i, out test) || test != myTestValue2) throw new ApplicationException();

                Trace.TraceInformation("{0} seek#2  {1} in {2}", name, count, time.ElapsedMilliseconds);
                time.Reset();
                time.Start();

                int tmpCount = 0;
                foreach (KeyValuePair<int, string> tmp in data)
                    if (tmp.Value != myTestValue2) throw new ApplicationException();
                    else tmpCount++;
                if (tmpCount != count) throw new ApplicationException();

                Trace.TraceInformation("{0} foreach {1} in {2}", name, count, time.ElapsedMilliseconds);
                time.Reset();
                time.Start();

                for (int i = start; i != stop; i += incr)
                    if (!data.Remove(i)) throw new ApplicationException();

                Trace.TraceInformation("{0} delete  {1} in {2}", name, count, time.ElapsedMilliseconds);

                for (int i = start; i != stop; i += incr)
                    if (data.TryGetValue(i, out test)) throw new ApplicationException();
            }
        }
        public Dictionary<int, string> TestMergeRandom(BPlusTreeOptions<int, string> options, int nsets, int nsize)
        {
            Dictionary<int, string> test = new Dictionary<int, string>();
            IEnumerable<KeyValuePair<int, string>>[] sets = 
                new List<IEnumerable<KeyValuePair<int, string>>>(CreateSets(nsets, nsize, test)).ToArray();

            using (BPlusTree<int, string> tree = new BPlusTree<int, string>(options))
            {
                foreach(IEnumerable<KeyValuePair<int, string>> set in sets)
                    tree.BulkInsert(set, new BulkInsertOptions { DuplicateHandling = DuplicateHandling.LastValueWins });

                VerifyDictionary(test, tree);

                tree.UnloadCache();
                tree.Add(int.MaxValue, "max");
                tree.Remove(int.MaxValue);

                VerifyDictionary(test, tree);
            }

            return test;
        }
        void TestRecoveryOnNew(BPlusTree<Guid, TestInfo>.OptionsV2 options, int count, int added)
        {
            BPlusTree<Guid, TestInfo> tree = null;
            var temp = TempFile.Attach(options.FileName);
            Dictionary<Guid, TestInfo> data = new Dictionary<Guid, TestInfo>();
            try
            {
                Assert.IsNotNull(options.TransactionLog);
                temp.Delete();
                tree = new BPlusTree<Guid, TestInfo>(options);
                using (var log = options.TransactionLog)
                {
                    using ((IDisposable)new PropertyValue(tree, "_storage").Value)
                        Insert(tree, data, Environment.ProcessorCount, count, TimeSpan.MaxValue);
                    //Add extra data...
                    AppendToLog(log, TestInfo.Create(added, data));
                }
                tree = null;
                //No data... yet...
                using(TempFile testempty = TempFile.FromCopy(options.FileName))
                {
                    var testoptions = options.Clone();
                    testoptions.TransactionLogFileName = null;
                    testoptions.TransactionLog = null;
                    testoptions.FileName = testempty.TempPath;

                    using (var empty = new BPlusTree<Guid, TestInfo>(testoptions))
                    {
                        empty.EnableCount();
                        Assert.AreEqual(0, empty.Count);
                    }
                }

                //Now recover...
                using (var recovered = new BPlusTree<Guid, TestInfo>(options))
                {
                    TestInfo.AssertEquals(data, recovered);
                }
            }
            finally
            {
                temp.Dispose();
                if (tree != null)
                    tree.Dispose();
            }
        }
        private static void VerifyDictionary(Dictionary<int, string> expected, BPlusTree<int, string> tree)
        {
            tree.Validate();
            tree.EnableCount();

            Dictionary<int, string> test = new Dictionary<int, string>(expected);
            List<KeyValuePair<int, string>> pairs = new List<KeyValuePair<int, string>>(test);

            string val;
            foreach (KeyValuePair<int, string> pair in tree)
            {
                Assert.IsTrue(test.TryGetValue(pair.Key, out val));
                Assert.AreEqual(pair.Value, val);
                Assert.IsTrue(test.Remove(pair.Key));
            }
            Assert.AreEqual(0, test.Count);
            test = null;
            Assert.IsNull(test);
            Assert.AreEqual(pairs.Count, tree.Count);

            foreach (KeyValuePair<int, string> pair in pairs)
            {
                Assert.IsTrue(tree.TryGetValue(pair.Key, out val));
                Assert.AreEqual(pair.Value, val);
            }
        }
 void AddStuff(BPlusTree<Guid, TestInfo> tree)
 {
     while (!mreStop.WaitOne(0, false))
     {
         foreach(var pair in CreateData(100))
             tree.Add(pair.Key, pair.Value);
         AddIdle(tree);
     }
 }
        public void TestMergeRandomInFile()
        {
            BPlusTreeOptions<int, string> options = Options;
            using (TempFile temp = new TempFile())
            {
                temp.Delete();
                options.CreateFile = CreatePolicy.Always;
                options.FileName = temp.TempPath;
                options.CalcBTreeOrder(4, 4);
                Stopwatch sw = Stopwatch.StartNew();
                
                Dictionary<int, string> expected = 
                    TestMergeRandom(options, 2, 300);

                Trace.TraceInformation("Creating {0} nodes in {1}.", expected.Count, sw.Elapsed);
                sw = Stopwatch.StartNew();

                options = Options;
                options.CreateFile = CreatePolicy.Never;
                options.FileName = temp.TempPath;
                options.CalcBTreeOrder(4, 4);
                using (BPlusTree<int, string> tree = new BPlusTree<int, string>(options))
                {
                    VerifyDictionary(expected, tree);
                }

                Trace.TraceInformation("Verified {0} nodes in {1}.", expected.Count, sw.Elapsed);
            }
        }