Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.OverflowException when backward iterating #1

Closed
cmerat opened this issue Dec 22, 2015 · 13 comments
Closed

System.OverflowException when backward iterating #1

cmerat opened this issue Dec 22, 2015 · 13 comments

Comments

@cmerat
Copy link

cmerat commented Dec 22, 2015

We are getting OverflowExceptions intermittently. It does not happen often but it's been reproduced a number of times by different machines meaning it is not an isolated incident. You'll find the stack below.

We're using <string, byte[]> for key/value store (string for key, byte[] since it's a custom serializer using JSON.NET).

Only occurs during execution. If we reload the database from the disk, the error no longer occurs.

System.OverflowException: Arithmetic operation resulted in an overflow.
   at DBreeze.Storage.FSR.Table_Read(Boolean useCache, Int64 offset, Int32 count) in c:\dev\DBreeze\DBreeze\Storage\FSR.cs:line 1040
   at DBreeze.Storage.FSR.Table_Read(Boolean useCache, Byte[] offset, Int32 count) in c:\dev\DBreeze\DBreeze\Storage\FSR.cs:line 854
   at DBreeze.Storage.StorageLayer.Table_Read(Boolean useCache, Byte[] offset, Int32 quantity) in c:\dev\DBreeze\DBreeze\Storage\StorageLayer.cs:line 134
   at DBreeze.LianaTrie.LTrieWriteCache.ReadKey(Boolean useCache, Byte[] pointer) in c:\dev\DBreeze\DBreeze\LianaTrie\LTrieWriteCache.cs:line 1187
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 70
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<ItBwd>d__0.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 92
   at DBreeze.LianaTrie.Iterations.Backward.<IterateBackward>d__10.MoveNext() in c:\dev\DBreeze\DBreeze\LianaTrie\Iterations\Backward.cs:line 176
   at DBreeze.Transactions.Transaction.<SelectBackward>d__2b`2.MoveNext() in c:\dev\DBreeze\DBreeze\Transactions\Transaction.cs:line 1520
@hhblaze
Copy link
Owner

hhblaze commented Dec 23, 2015

Hi, I need to be able to reproduce this exception. We don't have such problem on a very big park of machines and databases.
So, may be you can send me specifications of your PCs and working environment. This table physical file. May be also code, which you use to retrieve data from the table. And code which you use to insert data into table. Otherwise it's hard to imagine how I can help.

@hhblaze
Copy link
Owner

hhblaze commented Dec 23, 2015

Is it so, that some threads are writing data into the table, while other reading backward? As more info you supply - as better.

@cmerat cmerat changed the title System.OverflowException when background iterating System.OverflowException when backward iterating Dec 23, 2015
@cmerat
Copy link
Author

cmerat commented Dec 23, 2015

@hhblaze this appears to occur with a number of customers on various hardware so it's not an isolated incident. We've also managed to have a single dev reproduce the issue locally. However, since the issue doesn't appear to persist in the actual table files, it's almost impossible to reproduce.

You are correct however that this is in a multithreaded scenario. Our application uses heavy multithreading with multiple readers and writers concurrently. Multithreaded support was the main reason for adopting DBreeze.

I'll see about providing code samples. In the meantime, the most common scenario is the following:

  • single thread writing multiple rows and commiting transactions at intervals (to avoid overhead of committing for each entry)
  • multiple threads recovering single entries via key lookup
  • single thread iterating backwards through table

@hhblaze
Copy link
Owner

hhblaze commented Dec 23, 2015

I would say, that we need an emulator of your business case or, may be, your team can enhance source code with more informative exception handling.

@hhblaze
Copy link
Owner

hhblaze commented Dec 24, 2015

Some more questions:

  • Which .NET Framework version is used in DBreeze environment?
  • Since when and from which version do you use DBreeze for the table where mistake raises up?
  • Can you supply me with that table (if there is no customer/company sensitive data). I want to check either file is corrupted or not.
  • Would be nice if your dev team can build up an emulator of filling an reading data from that table. Emulate reading and writing threads and their quantity. I would like also to play with DBreeze before. For that I need to know the approximate structure of the key/value. I need to understand the backward lookup logic (how deep (last value, last 100 values), how often (once per second) etc). All other necessary info to build up an image of the emulator.

@hhblaze
Copy link
Owner

hhblaze commented Dec 25, 2015

During 24 hours was running a test that showed no mistakes.

Once per second was an insert of 5 values (key is string from Guid, value is byte[400]). 10 threads were trying to get single existing value in random time interval from 700ms up 2500ms. And other 10 threads in random time interval were trying to get SelectBackward last 150 values.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using DBreeze;
using DBreeze.Utils;



namespace testB
{
    public partial class Form1 : Form
    {

        DBreezeEngine engine = null;

        public Form1()
        {
            InitializeComponent();


            engine = new DBreezeEngine(@"E:\temp\DBreezeTest\DBR1");
        }


        List<string> d = new List<string>();
        int eq = 10000000;

        private void button1_Click(object sender, EventArgs e)
        {

            for (int i = 0; i < eq; i++)
            {
                d.Add(Guid.NewGuid().ToString());
            }

            Console.WriteLine("Filled");

            System.Threading.Tasks.Task.Run(() =>
            {
                while (true)
                {
                    Insert();
                    System.Threading.Thread.Sleep(1000);
                }

            });

            Random rnd = new Random();
            for (int ti = 0; ti < 10; ti++)
            {
                System.Threading.Tasks.Task.Run(() =>
                {
                    while (true)
                    {
                        SingleReader();
                        System.Threading.Thread.Sleep(700 + rnd.Next(2000));
                    }

                });
            }

            for (int i = 0; i < 5; i++)
            {
                System.Threading.Tasks.Task.Run(() =>
                {
                    while (true)
                    {
                        BackReader();
                        System.Threading.Thread.Sleep(1500 + rnd.Next(2000));
                    }

                });
            }

            for (int i = 0; i < 5; i++)
            {
                System.Threading.Tasks.Task.Run(() =>
                {
                    while (true)
                    {
                        BackReader2();
                        System.Threading.Thread.Sleep(1500 + rnd.Next(2000));
                    }

                });
            }

        }


        private void BackReader()
        {
            try
            {
                using (var tran = engine.GetTransaction())
                {
                   foreach(var row in tran.SelectBackward<string, byte[]>("t1").Take(150))
                   {
                       var val = row.Value;
                   }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }


        private void BackReader2()
        {
            try
            {
                using (var tran = engine.GetTransaction())
                {
                    tran.ValuesLazyLoadingIsOn = false;

                    foreach (var row in tran.SelectBackward<string, byte[]>("t1").Take(150))
                    {
                        var val = row.Value;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }



        int jj = 0;

        private void SingleReader()
        {
            try
            {
                if (jj < 1)
                    return;

                using (var tran = engine.GetTransaction())
                {
                    var rnd = new System.Random();
                    var p = rnd.Next(jj - 1);
                    var rrr = tran.Select<string, byte[]>("t1", d[p]);
                    var val = rrr.Value;                  
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }


        private void Insert()
        {
            try
            {

                using(var tran = engine.GetTransaction())
                {
                    for (int i = 0; i < 5; i++)
                    {
                        tran.Insert<string, byte[]>("t1", d[jj++], new byte[400]);
                    }

                    tran.Commit();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());

            }
        }



    }
}

@cmerat
Copy link
Author

cmerat commented Dec 26, 2015

@hhblaze I'll be sure to send you a sample that reproduces exactly our usage pattern as soon as possible. I'm currently away from the office for the holidays but as soon as I can, I'll send something your way.

This is not an issue that occurs frequently. It's pretty much assuredly a race condition since we barely see it in our customers and it's running essentially continuously for hours at a time across all of our customers. Repro cases are rare but we've gotten to the point where we do get a significant enough number of occurrences since we have a large customer base.

@cmerat
Copy link
Author

cmerat commented Jan 4, 2016

@hhblaze I've written code that pretty much systematically reproduces the case. You must just be sure to use an empty directory. It appears that it's the act of adding multiple tables that is causing the problem. At any rate, start with an empty folder and run the following code and it will fail every single time (at least, I've been able to reproduce 100%).

    private const string DataFolder = @"C:\Temp\CorruptDBreezeAttempt";
    private const string SessionIdPrefix = "SESSION";
    private const int MaxSessionCount = 5;
    private const int MaxItemId = 100000;
    private const int MaxPendingUpdates = 1000;
    private const int MaxInsertThreadCount = 64;
    private const int MaxReaderThreadCount = 10;

    private static bool _doUpdates = true;
    private static readonly ConcurrentQueue<Item> _pendingUpdates = new ConcurrentQueue<Item>();
    private static readonly Random _random = new Random();
    private static DBreezeEngine _engine;

    static void Main(string[] args)
    {
        // sets up serialization to support using JSON
        CustomSerializator.Serializator = ToJson;
        CustomSerializator.Deserializator = FromJson;

        using (_engine = new DBreezeEngine(DataFolder))
        {
            var threads = new List<Thread>();

            var updateThread = new Thread(UpdateLoop)
                {
                    Name = "Update Worker Thread",
                    IsBackground = true,
                };
            updateThread.Start();
            threads.Add(updateThread);

            for (var i = 0; i < MaxInsertThreadCount; i++)
            {
                new Thread(InsertLoop)
                    {
                        Name = "Insert Worker Thread " + i,
                        IsBackground = true,
                    }.Start();
            }

            for (var i = 0; i < MaxReaderThreadCount; i++)
            {
                var readThread = new Thread(ReadLoop)
                    {
                        Name = "Read Worker Thread " + i,
                    };
                readThread.Start();
                threads.Add(readThread);
            }

            Console.WriteLine("Press any key to stop");
            Console.Read();

            Console.WriteLine("Stopping...");
            _doUpdates = false;

            // wait on all threads to be done with engine before disposing
            foreach (var thread in threads)
            {
                thread.Join();
            }
        }

        Console.WriteLine();
        Console.WriteLine("----------");
        Console.WriteLine("Done!");
        Console.ReadLine();
    }

    private static void ReadLoop()
    {
        while (_doUpdates)
        {
            try
            {
                var sessionId = SessionIdPrefix + _random.Next(MaxSessionCount);

                if (_engine.Scheme.IfUserTableExists(GetItemsTableName(sessionId)))
                {
                    // only get a single item ~10% of the time
                    if (_random.Next(10) % 10 == 3)
                    {
                        var uniqueKey = "UniqueKey_" + _random.Next(MaxItemId);
                        var item = GetByUniqueKey(sessionId, uniqueKey);
                        if (item != null)
                        {
                            Console.WriteLine("Item found: {0}", item.Id);
                        }
                        else
                        {
                            Console.WriteLine("Item with unique key {0} could not be found", uniqueKey);
                        }
                    }
                    else
                    {
                        var items = GetItems(sessionId, _random.Next(2) == 1, 0, _random.Next(1000) + 10);
                        Console.WriteLine("{0} items returned in page", items.Count());
                    }
                }

                Thread.Sleep(_random.Next(500) + 10);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error while loading items: {0}", ex);
                File.AppendAllText(Path.Combine(DataFolder, "!Log.txt"), ex + Environment.NewLine + "--------------------" + Environment.NewLine);
            }
        }
    }

    private static void InsertLoop()
    {
        while (_doUpdates)
        {
            // don't add too many updates in memory at a time
            if (_pendingUpdates.Count < MaxPendingUpdates)
            {
                var id = _random.Next(MaxItemId);
                var sessionId = SessionIdPrefix + _random.Next(MaxSessionCount);

                var item = new Item
                    {
                        Id = id.ToString(),
                        SessionId = sessionId,
                        UniqueKey = "UniqueKey_" + id,
                        Value = _random.Next(int.MaxValue),
                    };

                _pendingUpdates.Enqueue(item);
            }

            Thread.Sleep(_random.Next(250));
        }
    }

    private static void UpdateLoop()
    {
        while (_doUpdates)
        {
            try
            {
                if (!DoUpdates())
                {
                    Thread.Sleep(100);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error when saving Item: {0}", ex);
                Thread.Sleep(100);
            }
        }
    }

    private static bool DoUpdates()
    {
        var maxTime = TimeSpan.FromSeconds(2);
        Transaction trans = null;
        DateTime? startTime = null;
        Item item;

        while (_pendingUpdates.TryDequeue(out item))
        {
            var itemsTable = GetItemsTableName(item.SessionId);
            var itemsUniqueKeyTable = GetUniqueKeyTableName(item.SessionId);

            trans = trans ?? _engine.GetTransaction();
            startTime = startTime ?? DateTime.Now;

            var row = trans.Select<string, DbCustomSerializer<Item>>(itemsTable, item.Id);
            if (row.Exists)
            {
                // we need to make sure to include any data that may have been saved already but we aren't aware of
                MergeItem(item, row.Value.Get);
            }

            byte[] ptr;
            trans.Insert<string, DbCustomSerializer<Item>>(itemsTable, item.Id, item, out ptr);

            // if the unique key is specified, index it for quick recovery
            if (!string.IsNullOrEmpty(item.UniqueKey))
            {
                trans.Insert(itemsUniqueKeyTable, item.UniqueKey, ptr);
            }

            // don't keep the write buffer open too long -- 2 seconds is the maximum (so that Gets can reflect the changes of the last 2 seconds)
            if (DateTime.Now.Subtract(startTime.Value) > maxTime)
            {
                break;
            }
        }

        if (trans != null)
        {
            trans.Commit();
            trans.Dispose();

            return true;
        }

        return false;
    }

    private static string GetItemsTableName(string sessionId)
    {
        return "Items_" + sessionId;
    }

    private static string GetUniqueKeyTableName(string sessionId)
    {
        return "ItemsByUniqueKey_" + sessionId;
    }

    private static void MergeItem(Item newItem, Item existingItem)
    {
        newItem.Value = Math.Max(newItem.Value, existingItem.Value);
    }

    private static IEnumerable<Item> GetItems(string sessionId, bool useDescending, int pageNumber, int itemsPerPage)
    {
        using (var trans = _engine.GetTransaction())
        {
            // based on direction of natural sort order, use ascending or descending selection
            var tableName = GetItemsTableName(sessionId);
            var selector = useDescending
                ? trans.SelectBackward<string, DbCustomSerializer<Item>>(tableName)
                : trans.SelectForward<string, DbCustomSerializer<Item>>(tableName);

            // since we're using the natural sort order, this gives us a LOT more flexibility with what we need to do
            // before we can determine if a row must be deserialized or not. as such, we can optimize pretty heavily!
            var leftToSkip = itemsPerPage * Math.Max(pageNumber, 0);
            var leftToReturn = Math.Min(itemsPerPage, (int)trans.Count(tableName));

            foreach (var row in selector)
            {
                if (leftToSkip <= 0)
                {
                    yield return row.Value.Get;

                    leftToReturn--;
                    if (leftToReturn <= 0)
                    {
                        // we've returned our page, done!
                        yield break;
                    }
                }
                else
                {
                    leftToSkip--;
                }
            }
        }
    }

    private static Item GetByUniqueKey(string sessionId, string uniqueKey)
    {
        using (var trans = _engine.GetTransaction())
        {
            var row = trans.Select<string, byte[]>(GetUniqueKeyTableName(sessionId), uniqueKey);
            if (row.Exists)
            {
                var directRow = trans.SelectDirect<string, DbCustomSerializer<Item>>(GetItemsTableName(sessionId), row.Value);
                if (directRow.Exists)
                {
                    return directRow.Value.Get;
                }
            }

            return null;
        }
    }

    /// <summary>
    /// Returns the JSON representation of the value.
    /// </summary>
    /// <param name="value">The value.</param>
    /// <returns>The JSON representation of the value.</returns>
    public static string ToJson(object value)
    {
        var settings = new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Auto,
            DefaultValueHandling = DefaultValueHandling.Ignore,
        };

        return JsonConvert.SerializeObject(value, settings);
    }

    /// <summary>
    /// Parses the JSON to return the actual object.
    /// </summary>
    /// <param name="json">The json.</param>
    /// <param name="type">The type.</param>
    /// <returns>The parsed object.</returns>
    public static object FromJson(string json, Type type)
    {
        var settings = new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Auto,
            DefaultValueHandling = DefaultValueHandling.Ignore,
        };

        return JsonConvert.DeserializeObject(json, type, settings);
    }

    private class Item
    {
        public string Id { get; set; }
        public string SessionId { get; set; }
        public string UniqueKey { get; set; }
        public int Value { get; set; }
    }

@cmerat
Copy link
Author

cmerat commented Jan 4, 2016

@hhblaze Another note: this only reproduces in the case where there are multiple tables involved. Looking at the code it appears that it is possible for it to occur even in the case of a single session but I haven't been able to reproduce it as such.

It also appears that the system eventually recovers since later requests function correctly.

@hhblaze
Copy link
Owner

hhblaze commented Jan 5, 2016

Very well, will take a look

@hhblaze
Copy link
Owner

hhblaze commented Jan 8, 2016

Issue was fixed in uploaded version 1.073. Please, try it.

@cmerat
Copy link
Author

cmerat commented Jan 8, 2016

@hhblaze I've merged your changes into our fork and it appears to be working well! I'll let you know if we run into other issues but the fix looks good! Thanks!

@hhblaze
Copy link
Owner

hhblaze commented Jan 8, 2016

Good luck!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants