func flush_ttl(srv *clonesrv_t) (err error) { for _, kvmsg := range srv.kvmap { // If key-value pair has expired, delete it and publish the // fact to listening clients. if ttls, e := kvmsg.GetProp("ttl"); e == nil { ttl, e := strconv.ParseInt(ttls, 10, 64) if e != nil { err = e continue } if time.Now().After(time.Unix(ttl, 0)) { srv.sequence++ kvmsg.SetSequence(srv.sequence) kvmsg.SetBody("") e = kvmsg.Send(srv.publisher) if e != nil { err = e } kvmsg.Store(srv.kvmap) log.Println("I: publishing delete =", srv.sequence) } } } return }
func collector(srv *clonesrv_t) (err error) { kvmsg, err := kvmsg.RecvKvmsg(srv.collector) if err != nil { return } if srv.active { srv.sequence++ kvmsg.SetSequence(srv.sequence) kvmsg.Send(srv.publisher) if ttls, e := kvmsg.GetProp("ttl"); e == nil { ttl, e := strconv.ParseInt(ttls, 10, 64) if e != nil { err = e return } kvmsg.SetProp("ttl", fmt.Sprint(time.Now().Add(time.Duration(ttl)*time.Second).Unix())) } kvmsg.Store(srv.kvmap) log.Println("I: publishing update =", srv.sequence) } else { // If we already got message from active, drop it, else // hold on pending list if !srv.was_pending(kvmsg) { srv.pending = append(srv.pending, kvmsg) } } 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 (agent *agent_t) control_message() (err error) { msg, e := agent.pipe.RecvMessage(0) if e != nil { return e } command := msg[0] msg = msg[1:] switch command { case "SUBTREE": agent.subtree = msg[0] case "CONNECT": address := msg[0] service := msg[1] if agent.nbr_servers < server_MAX { serv, _ := strconv.Atoi(service) agent.server[agent.nbr_servers] = server_new(address, serv, agent.subtree) agent.nbr_servers++ // We broadcast updates to all known servers agent.publisher.Connect(fmt.Sprintf("%s:%d", address, serv+2)) } else { fmt.Printf("E: too many servers (max. %d)\n", server_MAX) } case "SET": // When we set a property, we push the new key-value pair onto // all our connected servers: key := msg[0] value := msg[1] ttl := msg[2] // Send key-value pair on to server kvmsg := kvmsg.NewKvmsg(0) kvmsg.SetKey(key) kvmsg.SetUuid() kvmsg.SetBody(value) kvmsg.SetProp("ttl", ttl) kvmsg.Store(agent.kvmap) kvmsg.Send(agent.publisher) case "GET": key := msg[0] value := "" if kvmsg, ok := agent.kvmap[key]; ok { value, _ = kvmsg.GetBody() } agent.pipe.SendMessage(value) } return }
func collector(srv *clonesrv_t) (err error) { kvmsg, err := kvmsg.RecvKvmsg(srv.collector) if err != nil { return } srv.sequence++ kvmsg.SetSequence(srv.sequence) kvmsg.Send(srv.publisher) if ttls, e := kvmsg.GetProp("ttl"); e == nil { // change duration into specific time, using the same property: ugly! ttl, e := strconv.ParseInt(ttls, 10, 64) if e != nil { err = e return } kvmsg.SetProp("ttl", fmt.Sprint(time.Now().Add(time.Duration(ttl)*time.Second).Unix())) } kvmsg.Store(srv.kvmap) log.Println("I: publishing update =", srv.sequence) return }
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 } } }
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) }