func snapshots(srv *clonesrv_t) (err error) { msg, err := srv.snapshot.RecvMessage(0) if err != nil { return } identity := msg[0] // Request is in second frame of message request := msg[1] if request != "ICANHAZ?" { err = errors.New("E: bad request, aborting") return } subtree := msg[2] // Send state socket to client for _, kvmsg := range srv.kvmap { if key, _ := kvmsg.GetKey(); strings.HasPrefix(key, subtree) { srv.snapshot.Send(identity, zmq.SNDMORE) kvmsg.Send(srv.snapshot) } } // Now send END message with sequence number log.Println("I: sending shapshot =", srv.sequence) srv.snapshot.Send(identity, zmq.SNDMORE) kvmsg := kvmsg.NewKvmsg(srv.sequence) kvmsg.SetKey("KTHXBAI") kvmsg.SetBody(subtree) kvmsg.Send(srv.snapshot) return }
func subscriber(srv *clonesrv_t) (err error) { // Get state snapshot if necessary if !srv.kvmap_init { srv.kvmap_init = true snapshot, _ := zmq.NewSocket(zmq.DEALER) snapshot.Connect(fmt.Sprint("tcp://localhost:", srv.peer)) fmt.Printf("I: asking for snapshot from: tcp://localhost:%v\n", srv.peer) snapshot.SendMessage("ICANHAZ?", "") // blank subtree to get all for { kvmsg, e := kvmsg.RecvKvmsg(snapshot) if e != nil { err = e break } if key, _ := kvmsg.GetKey(); key == "KTHXBAI" { srv.sequence, _ = kvmsg.GetSequence() break // Done } kvmsg.Store(srv.kvmap) } fmt.Println("I: received snapshot =", srv.sequence) } // Find and remove update off pending list kvmsg, e := kvmsg.RecvKvmsg(srv.subscriber) if e != nil { err = e return } if key, _ := kvmsg.GetKey(); key != "HUGZ" { if !srv.was_pending(kvmsg) { // If active update came before client update, flip it // around, store active update (with sequence) on pending // list and use to clear client update when it comes later srv.pending = append(srv.pending, kvmsg) } // If update is more recent than our kvmap, apply it if seq, _ := kvmsg.GetSequence(); seq > srv.sequence { srv.sequence = seq kvmsg.Store(srv.kvmap) fmt.Println("I: received update =", srv.sequence) } } return }
func main() { snapshot, _ := zmq.NewSocket(zmq.DEALER) snapshot.Connect("tcp://localhost:5556") subscriber, _ := zmq.NewSocket(zmq.SUB) subscriber.SetSubscribe(SUBTREE) subscriber.Connect("tcp://localhost:5557") publisher, _ := zmq.NewSocket(zmq.PUSH) publisher.Connect("tcp://localhost:5558") kvmap := make(map[string]*kvmsg.Kvmsg) rand.Seed(time.Now().UnixNano()) // We first request a state snapshot: sequence := int64(0) snapshot.SendMessage("ICANHAZ?", SUBTREE) for { kvmsg, err := kvmsg.RecvKvmsg(snapshot) if err != nil { break // Interrupted } if key, _ := kvmsg.GetKey(); key == "KTHXBAI" { sequence, _ := kvmsg.GetSequence() fmt.Println("I: received snapshot =", sequence) break // Done } kvmsg.Store(kvmap) } snapshot.Close() poller := zmq.NewPoller() poller.Add(subscriber, zmq.POLLIN) alarm := time.Now().Add(1000 * time.Millisecond) for { tickless := alarm.Sub(time.Now()) if tickless < 0 { tickless = 0 } polled, err := poller.Poll(tickless) if err != nil { break // Context has been shut down } if len(polled) == 1 { kvmsg, err := kvmsg.RecvKvmsg(subscriber) if err != nil { break // Interrupted } // Discard out-of-sequence kvmsgs, incl. heartbeats if seq, _ := kvmsg.GetSequence(); seq > sequence { sequence = seq kvmsg.Store(kvmap) fmt.Println("I: received update =", sequence) } } // If we timed-out, generate a random kvmsg if time.Now().After(alarm) { kvmsg := kvmsg.NewKvmsg(0) kvmsg.SetKey(fmt.Sprintf("%s%d", SUBTREE, rand.Intn(10000))) kvmsg.SetBody(fmt.Sprint(rand.Intn(1000000))) kvmsg.SetProp("ttl", fmt.Sprintf("%d", rand.Intn((30)))) // seconds kvmsg.Send(publisher) alarm = time.Now().Add(1000 * time.Millisecond) } } fmt.Printf("Interrupted\n%d messages in\n", sequence) }
func clone_agent(pipename string) { pipe, _ := zmq.NewSocket(zmq.PAIR) pipe.Connect(pipename) agent := agent_new(pipe) LOOP: for { poller := zmq.NewPoller() poller.Add(pipe, zmq.POLLIN) server := agent.server[agent.cur_server] switch agent.state { case state_INITIAL: // In this state we ask the server for a snapshot, // if we have a server to talk to... if agent.nbr_servers > 0 { fmt.Printf("I: waiting for server at %s:%d...\n", server.address, server.port) if server.requests < 2 { server.snapshot.SendMessage("ICANHAZ?", agent.subtree) server.requests++ } server.expiry = time.Now().Add(server_TTL) agent.state = state_SYNCING poller.Add(server.snapshot, zmq.POLLIN) } case state_SYNCING: // In this state we read from snapshot and we expect // the server to respond, else we fail over. poller.Add(server.snapshot, zmq.POLLIN) case state_ACTIVE: // In this state we read from subscriber and we expect // the server to give hugz, else we fail over. poller.Add(server.subscriber, zmq.POLLIN) break } poll_timer := time.Duration(-1) if server != nil { poll_timer = server.expiry.Sub(time.Now()) if poll_timer < 0 { poll_timer = 0 } } // We're ready to process incoming messages; if nothing at all // comes from our server within the timeout, that means the // server is dead: polled, err := poller.Poll(poll_timer) if err != nil { break } if len(polled) > 0 { for _, item := range polled { switch socket := item.Socket; socket { case pipe: err = agent.control_message() if err != nil { break LOOP } default: kvmsg, e := kvmsg.RecvKvmsg(socket) if e != nil { err = e break LOOP } // Anything from server resets its expiry time server.expiry = time.Now().Add(server_TTL) if agent.state == state_SYNCING { // Store in snapshot until we're finished server.requests = 0 if key, _ := kvmsg.GetKey(); key == "KTHXBAI" { agent.sequence, _ = kvmsg.GetSequence() agent.state = state_ACTIVE fmt.Printf("I: received from %s:%d snapshot=%d\n", server.address, server.port, agent.sequence) } else { kvmsg.Store(agent.kvmap) } } else if agent.state == state_ACTIVE { // Discard out-of-sequence updates, incl. hugz if seq, _ := kvmsg.GetSequence(); seq > agent.sequence { agent.sequence = seq kvmsg.Store(agent.kvmap) fmt.Printf("I: received from %s:%d update=%d\n", server.address, server.port, agent.sequence) } } } } } else { // Server has died, failover to next fmt.Printf("I: server at %s:%d didn't give hugz\n", server.address, server.port) agent.cur_server = (agent.cur_server + 1) % agent.nbr_servers agent.state = state_INITIAL } } }