4

Working with GraphEngine for a while I very often find myself with a deadlocked thread while doing some operation against GraphEngine. Nested calls are definitely not on the menu. But now I encountered something strange:

foreach(long cellID ...)
{
    byte[] buffer;

    // the next line will block on the 54th call...
    Global.LocalStorage.LoadCell(cellID, out buffer);
}

Suspecting a non-existing cellID I wrapped the call with

if(Global.LocalStorage.Contains(cellID))
{ ... }

But now this call blocks indefinitly.

Is this a bug? or

Under which conditions would the call block?

Cheerio, Andreas.

1 Answers1

3

@Andreas Hassmann, I had met a similar issue, so I guess your trouble may be caused by the ... in the foreach-loop.

I used an iterator like Global.LocalStorage.xxxCell_Accessor_Selector().Select(c => c.CellID.Value). In this case, the locks of the storage haven't been released which causes the deadlock when Global.LocalStorage.LoadCell() acquires the locks.

If your problem is exact the same as mine, the solution will be to apply .ToList() to the iterator.

Here are my codes to reproduce your problem.

The TSL:

cell struct MyCell 
{
    int A; 
}

The codes:

for (int i = 0; i < 100; i++)
{
    MyCell mc = new MyCell(i);
    Global.LocalStorage.SaveMyCell(i, mc);
}

var ids = Global.LocalStorage.MyCell_Accessor_Selector().Select(c => c.CellID.Value);

Console.WriteLine("1 start.");
foreach (long cellID in ids.ToList())
{
    byte[] buffer;
    Global.LocalStorage.LoadCell(cellID, out buffer);
    Console.WriteLine(cellID);
}
Console.WriteLine("1 done.");

Console.WriteLine("2 start.");
foreach (long cellID in ids) 
{
    byte[] buffer;
    Global.LocalStorage.LoadCell(cellID, out buffer);
    Console.WriteLine(cellID);
}
Console.WriteLine("2 done.");
L. H.
  • 181
  • 4
  • Thanks, L.H.! I must apologize for abbreviating my question too far. My CellIDs came from iterating over a different CellType and the LoadCell blocked on the 54th call, not on the first. That's why I suspected an irregularity. But yes, I circumvented it with a ToList(), too. But this will render any clever iterator implementations within Trinity useless, in case they exist. – Andreas Hassmann Apr 01 '17 at 16:10
  • @AndreasHassmann I see what you want, there is another solution I found for it: if we are not to update any data within the iterator, we can just set Trinity storage as 'readonly' mode, which will enable arbitrary operations without worrying about the locks. My solution is first initializing the storage and saving storage to disk, then put these codes before loading and iterating the storage: `TrinityConfig.CurrentRunningMode = RunningMode.Embedded; TrinityConfig.ReadOnly = true; Global.LocalStorage.LoadStorage();` – L. H. Apr 05 '17 at 10:00
  • Again thanks, L.H. This sounds as if it could not be switched at runtime, right? I would have to reconnect to the database. Are you Liang He? I would like to talk about Trinity in general with someone who built it, without littering in the public space here... – Andreas Hassmann Apr 11 '17 at 21:10
  • Yes, you're right, we need to reconnect to it if we want to use 'readonly' mode after loading the data. And yes, I am Liang He. I have lots of practice on Trinity, and I'd like to share my experience about it here as I can. For more discussions, you may also post them here: https://github.com/Microsoft/GraphEngine/issues – L. H. Apr 12 '17 at 05:45