Thread safe tree library?

320 views
Skip to first unread message

joseph.p...@gmail.com

unread,
Jan 5, 2021, 6:52:20 PM1/5/21
to golang-nuts
Is there a simple tree library that is thread safe? 

If I have to write one for myself, do I have to set locks on nodes as I walk the tree to 
prevent another thread from changing nodes that are 'above' me?

K. Alex Mills

unread,
Jan 5, 2021, 7:40:54 PM1/5/21
to joseph.p...@gmail.com, golang-nuts
That is the simplest and most conservative way go about it, but ultimately it depends on what you need out of your concurrency semantics.

If you're willing to settle for a linearizable execution, you can gain some performance improvements by only checking the lock for write operations. So long as two write operations do not pass each other in the tree, they will be linearizable based on which write passed the root node first. Read operations can pass one another (and other write operations) freely. We can always pick a linearization point for them with respect to the rest of the concurrent write operations in the tree.

You will also need to do hand-over-hand locking on your way down the tree to ensure no two writes can overtake one another.

An approach like this can yield significant performance gains and you can formally show that the results you get are correct with regard to some actual state the tree could be in depending on when you consider each concurrent write to "complete".

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/6597198c-c439-4a1e-bff2-a2176204b272n%40googlegroups.com.

Nathan Fisher

unread,
Jan 5, 2021, 8:00:19 PM1/5/21
to K. Alex Mills, golang-nuts, joseph.p...@gmail.com
Does write only locking provide read correctness? I would’ve thought based on the memory model it could cause issues?


K. Alex Mills

unread,
Jan 5, 2021, 8:16:36 PM1/5/21
to Nathan Fisher, golang-nuts, joseph.p...@gmail.com
On Tue, Jan 5, 2021, 6:59 PM Nathan Fisher <nfi...@junctionbox.ca> wrote:
Does write only locking provide read correctness? I would’ve thought based on the memory model it could cause issues?


It depends on your notion of "read correctness", specifically when you consider each read to have occurred with respect to its concurrent writes. Linearizability may be a weaker guarantee than you want, and that's okay.

Linearizability requires that, for each operation, you can pick some point between the start and end of an operation when it can be said to have "occurred". When you consider all the operations in that order, the results you see must be the same as a sequential execution.

In the case I have described, we can pick a linearization point for reads just before the last write which they passed on their way down the tree. The reads should then see all the writes which happened prior to this point.

This isn't the order the operations enter the root, but linearizability doesn't care. It doesn't have an opinion on when overlapping operations "occur" with respect to one another.

I don't think using a happens-before relation for the program order seen by each goroutine is going to cause a problem with respect to choosing these linearization points, but maybe I'm missing something.

Maybe also there is a standardized notion of read correctness that you're referring to which I am not aware of.

joseph.p...@gmail.com

unread,
Jan 5, 2021, 9:51:58 PM1/5/21
to golang-nuts
Well, I think I only need to lock on writes, and it'll be easier if I just lock the entire tree on writes. Reads will be the majority of the operations by far. This is for a bit of caching before we go to a K/V database like REDIS, etc.

Robert Engels

unread,
Jan 6, 2021, 1:19:01 AM1/6/21
to joseph.p...@gmail.com, golang-nuts
I think you have to go a bit more and use a RW mutex to ensure memory consistency (for the simple solution). 

On Jan 5, 2021, at 8:52 PM, joseph.p...@gmail.com <joseph.p...@gmail.com> wrote:

Well, I think I only need to lock on writes, and it'll be easier if I just lock the entire tree on writes. Reads will be the majority of the operations by far. This is for a bit of caching before we go to a K/V database like REDIS, etc.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

ksbh...@gmail.com

unread,
Jan 6, 2021, 10:27:29 AM1/6/21
to golang-nuts
https://pkg.go.dev/lang.yottadb.com/go/yottadb gives you B*trees with a hierarchical key-value model that you can experiment with in a Docker container (or of course a virtual or real machine). When you get to needing persistence and concurrency control, you can also get ACID transactions (i.e., for linearization, it “occurs” at commit time).

Regards
– Bhaskar

Robert Engels

unread,
Jan 6, 2021, 4:09:34 PM1/6/21
to ksbh...@gmail.com, golang-nuts
You can look at github.com/robaho/keydb and it’s LSM trees as an alternative method of concurrency. You can also take the low level tree.go and wrap it in a RW mutex. 

On Jan 6, 2021, at 9:28 AM, ksbh...@gmail.com <ksbh...@gmail.com> wrote:



Nathan Fisher

unread,
Jan 8, 2021, 2:29:59 PM1/8/21
to K. Alex Mills, golang-nuts, joseph.p...@gmail.com
I was thinking of potential issues if you rebalance the tree as an example.

I’m not certain what issues could arise as I’ve never considered a concurrent data structure that lacks some kind of synchronisation for both read and writes unless it’s immutable copy-on-write or similar.

Do you happen to have references available for further research?

Robert Engels

unread,
Jan 8, 2021, 5:18:11 PM1/8/21
to Nathan Fisher, K. Alex Mills, golang-nuts, joseph.p...@gmail.com
I would review the source code of the Java concurrent package. It has links to the relevant academic research. 

On Jan 8, 2021, at 1:29 PM, Nathan Fisher <nfi...@junctionbox.ca> wrote:


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

K. Alex Mills

unread,
Jan 8, 2021, 5:56:46 PM1/8/21
to Robert Engels, Nathan Fisher, golang-nuts, joseph.p...@gmail.com
I was thinking of potential issues if you rebalance the tree as an example.

I’m not certain what issues could arise as I’ve never considered a concurrent data structure that lacks some kind of synchronisation for both read and writes unless it’s immutable copy-on-write or similar.

Do you happen to have references available for further research?
You do have to be careful when rebalancing. The goal is to make the rebalancing appear atomic to other reads as they go down the tree. IIRC a technique to do this involves recreating the nodes involved in the rebalance and completing the rebalance by updating a single edge at once. That sounds a lot to me like copy-on-write but IIRC it doesn't require copying the entire subtree.

You might also be interested in some of the below references, although they're all on lock-free and wait-free options. Disclaimer: They're all from my Ph.D advisor. I was working in his lab when most of this work was being done. Most of what I've posted here is based on my memory of their approach.

joseph.p...@gmail.com

unread,
Jan 10, 2021, 7:51:13 PM1/10/21
to golang-nuts
Thanks! You all have given me much to look at.

-joe

Reply all
Reply to author
Forward
0 new messages