FAQ
I'm implementing a distributed key value store. To test the implementation,
I'm trying to force a specific interleaving of go-routines.
Is this idiomatic Go ? Are there other ways to implement this ?

Design ( See RAMP transactions, Peter Bailis)
------------
1. Key value store consists of multiple server processes. Keys are sharded
across the servers
2. A write (put all) can span multiple servers and uses a 2 phase protocol
- Phase 1 : The write is prepared on each server (this is basically writing
to disk)
- Phase 2 : The write committed when all the servers in Phase1 have prepared
3. A read (get all) spans multiple servers
- Normally the committed value of a key is returned
- In failure cases, the servers maintain enough state to return prepared
writes with certain consistency guarantees

Test
--------------------
I want to a write a test where
1. Client1 submits a write to servers server1 and server2
2. Client1's write blocks after phase 1 (i.e after the write has been
prepared)
3. While Client1 is blocked, Client2 issues a read to servers 1 and 2
4. The goal of the test is to verify that despite client1's write not being
committed, client2 is able to observe the write

Implementation
--------------------
The servers are implemented using plain net/rpc.

type rpcServer struct {
    s store
}

type store struct {}

(rpc *rpcServer) PutAll(w *write, res *wresponse) error {
    rpc.s.PutAll(w, r)
}

(rpc *rpcServer) GetAll(r *read, res *rresponse) error {
   rpc.s.GetAll(r, res)
}

(s *store) PutAll(w *write, res *wresponse) error {
    s.Prepare(w, res)
    s.Commit(w, res)
}

To introduce delays in PutAll/GetAll, I use a test store

(s *delayStore) PutAll(w *write, res *wresponse) error {
    s.Prepare(w, res)
    s.afterPrepare.wait() //this is a sync.waitgroup
    s.Commit(w, res)
}

And finally in the test

s1 := // init rpc server1 with a delay store
s2 := // init rpc server2 with a delay store
c1 := // init client1 with s1 and s2
c2 := // init client2 with s1 and s2

s1.afterPrepare.wait(1)
go func() {
   c1.PutAll(write1, writeResponse1)
}

time.Sleep(5 * time.Millisecond) // non-deterministic sleep to wait till c1
is blocked after prepare

c2.GetAll(read1, readResponse1) // now, c2 is reading a write that has been
prepared but not committed by c1

s1.afterPrepare.done() //release c1

Is there a better/more idiomatic way to implement this test ?

Thanks.




--
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+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Search Discussions

  • Jason E. Aten at Mar 2, 2016 at 9:59 am
    I typically put each client / server on its own go-routine, then use
    channels to synchronize them (e.g. close a channel to broadcast to others
    that a given goroutine has reached a specific point). Once I'm satisfied
    under tests that the logic is correct between the communicating goroutines,
    then set a flag and have communication go over rpc instead.

    btw that's terrific that you are implementing RAMP transactions. Very cool.
    Where is your repo?

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedMar 2, '16 at 12:16a
activeMar 2, '16 at 9:59a
posts2
users2
websitegolang.org

2 users in discussion

Ephrim Stanley: 1 post Jason E. Aten: 1 post

People

Translate

site design / logo © 2022 Grokbase