// Start listening, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { // remember the channel to talk to microservices microservices := ribbon.MakeRouter() dependencies := make(map[string]time.Time) // dependent services and time last updated var parent chan gotocol.Message // remember how to talk back to creator requestor := make(map[string]gotocol.Routetype) // remember where requests came from when responding var name string // remember my name eureka := make(map[string]chan gotocol.Message, 1) // service registry hist := collect.NewHist("") ep, _ := time.ParseDuration(archaius.Conf.EurekaPoll) eurekaTicker := time.NewTicker(ep) for { select { case msg := <-listener: flow.Instrument(msg, name, hist) switch msg.Imposition { case gotocol.Hello: if name == "" { // if I don't have a name yet remember what I've been named parent = msg.ResponseChan // remember how to talk to my namer name = msg.Intention // message body is my name hist = collect.NewHist(name) } case gotocol.Inform: eureka[msg.Intention] = handlers.Inform(msg, name, listener) case gotocol.NameDrop: handlers.NameDrop(&dependencies, microservices, msg, name, listener, eureka) case gotocol.Forget: // forget a buddy handlers.Forget(&dependencies, microservices, msg) case gotocol.GetRequest: // route the request on to microservices handlers.GetRequest(msg, name, listener, &requestor, microservices) case gotocol.GetResponse: // return path from a request, send payload back up using saved span context - server send handlers.GetResponse(msg, name, listener, &requestor) case gotocol.Put: // route the request on to a random dependency handlers.Put(msg, name, listener, &requestor, microservices) case gotocol.Goodbye: for _, ch := range eureka { // tell name service I'm not going to be here ch <- gotocol.Message{gotocol.Delete, nil, time.Now(), gotocol.NilContext, name} } gotocol.Message{gotocol.Goodbye, nil, time.Now(), gotocol.NilContext, name}.GoSend(parent) return } case <-eurekaTicker.C: // check to see if any new dependencies have appeared for dep, _ := range dependencies { for _, ch := range eureka { ch <- gotocol.Message{gotocol.GetRequest, listener, time.Now(), gotocol.NilContext, dep} } } } } }
// Shutdown fsm and pirates func shutdown() { var msg gotocol.Message hist := collect.NewHist("fsm") // wait until the delay has finished if archaius.Conf.RunDuration >= time.Millisecond { time.Sleep(archaius.Conf.RunDuration) } log.Println("fsm: Shutdown") for _, noodle := range noodles { gotocol.Message{gotocol.Goodbye, nil, time.Now(), gotocol.NilContext, "beer volcano"}.GoSend(noodle) } for len(noodles) > 0 { msg = <-listener collect.Measure(hist, time.Since(msg.Sent)) if archaius.Conf.Msglog { log.Printf("fsm: %v\n", msg) } switch msg.Imposition { case gotocol.Goodbye: delete(noodles, msg.Intention) if archaius.Conf.Msglog { log.Printf("fsm: Pirate population: %v \n", len(noodles)) } } } collect.Save() log.Println("fsm: Exit") }
// Start the denominator, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { microservices := ribbon.MakeRouter() dependencies := make(map[string]time.Time) // dependent services and time last updated var parent chan gotocol.Message // remember how to talk back to creator var name string // remember my name nethist := collect.NewHist("") // don't know name yet - message network latency resphist := collect.NewHist("") // response time history servhist := collect.NewHist("") // service time history rthist := collect.NewHist("") // round trip history eureka := make(map[string]chan gotocol.Message, len(archaius.Conf.ZoneNames)*archaius.Conf.Regions) // service registry per zone and region var chatrate time.Duration ep, _ := time.ParseDuration(archaius.Conf.EurekaPoll) eurekaTicker := time.NewTicker(ep) chatTicker := time.NewTicker(time.Hour) chatTicker.Stop() w := 1 // counter for random messages for { select { case msg := <-listener: flow.Instrument(msg, name, nethist) switch msg.Imposition { case gotocol.Hello: if name == "" { // if I don't have a name yet remember what I've been named parent = msg.ResponseChan // remember how to talk to my namer name = msg.Intention // message body is my name nethist = collect.NewHist(name + "_net") resphist = collect.NewHist(name + "_resp") servhist = collect.NewHist(name + "_serv") rthist = collect.NewHist(name + "_rt") } case gotocol.Inform: eureka[msg.Intention] = handlers.Inform(msg, name, listener) case gotocol.NameDrop: handlers.NameDrop(&dependencies, microservices, msg, name, listener, eureka, true) case gotocol.Forget: // forget a buddy handlers.Forget(&dependencies, microservices, msg) case gotocol.Chat: // setup the ticker to run at the specified rate d, e := time.ParseDuration(msg.Intention) if e == nil && d >= time.Millisecond && d <= time.Hour { chatrate = d chatTicker = time.NewTicker(chatrate) } case gotocol.GetResponse: // return path from a request, terminate and log response time in histograms flow.End(msg, resphist, servhist, rthist) case gotocol.Goodbye: if archaius.Conf.Msglog { log.Printf("%v: Going away, was chatting every %v\n", name, chatrate) } collect.SaveHist(nethist, name, "_net") collect.SaveHist(resphist, name, "_resp") collect.SaveHist(servhist, name, "_serv") collect.SaveHist(rthist, name, "_rt") collect.SaveAllGuesses(name) gotocol.Message{gotocol.Goodbye, nil, time.Now(), gotocol.NilContext, name}.GoSend(parent) return } case <-eurekaTicker.C: // check to see if any new dependencies have appeared for dep, _ := range dependencies { for _, ch := range eureka { ch <- gotocol.Message{gotocol.GetRequest, listener, time.Now(), gotocol.NilContext, dep} } } case <-chatTicker.C: c := microservices.Random() if c != nil { ctx := gotocol.NewTrace() now := time.Now() var sm gotocol.Message switch rand.Intn(3) { case 0: sm = gotocol.Message{gotocol.GetRequest, listener, now, ctx, "why?"} case 1: q := rand.Intn(w) // pick a random key that has already been put sm = gotocol.Message{gotocol.GetRequest, listener, now, ctx, fmt.Sprintf("Why%v%v", q, q*q)} case 2: sm = gotocol.Message{gotocol.Put, listener, now, ctx, fmt.Sprintf("Why%v%v me", w, w*w)} w++ // put a new key each time } flow.AnnotateSend(sm, name) // service send logs creation time for this flow sm.GoSend(c) } } } }
// Start eureka discovery service and set name directly func Start(listener chan gotocol.Message, name string) { // use a waitgroup so whoever starts eureka can tell it's ready and when stopping that the logs have been flushed Wg.Add(1) defer Wg.Done() var msg gotocol.Message var ok bool hist := collect.NewHist(name) microservices := make(map[string]chan gotocol.Message, archaius.Conf.Dunbar) eurekaservices := make(map[string]chan gotocol.Message, 2) metadata := make(map[string]meta, archaius.Conf.Dunbar) lastrequest := make(map[callback]time.Time) // remember time of last request for a service from this requestor log.Println(name + ": starting") for { msg, ok = <-listener collect.Measure(hist, time.Since(msg.Sent)) if !ok { break // channel was closed } //commented out because name service traffic is too much noise in the log //if archaius.Conf.Msglog { // log.Printf("%v(backlog %v): %v\n", name, len(listener), msg) //} switch msg.Imposition { // used to wire up connections to other eureka nodes only case gotocol.NameDrop: if msg.Intention != name { // don't talk to myself eurekaservices[msg.Intention] = msg.ResponseChan } // for new nodes record the data, replicate and maybe pass on to be logged case gotocol.Put: if microservices[msg.Intention] == nil { // ignore duplicate requests microservices[msg.Intention] = msg.ResponseChan metadata[msg.Intention] = meta{true, msg.Sent} // replicate request, everyone ends up with the same timestamp for state change of this service for _, c := range eurekaservices { gotocol.Message{gotocol.Replicate, msg.ResponseChan, msg.Sent, gotocol.NilContext, msg.Intention}.GoSend(c) } if edda.Logchan != nil { edda.Logchan <- msg } } case gotocol.Replicate: if microservices[msg.Intention] == nil { // ignore multiple requests microservices[msg.Intention] = msg.ResponseChan metadata[msg.Intention] = meta{true, msg.Sent} } case gotocol.Inform: // don't store edges in discovery but do log them if edda.Logchan != nil { edda.Logchan <- msg } case gotocol.GetRequest: if msg.Intention == "" { log.Fatal(name + ": empty GetRequest") } if microservices[msg.Intention] != nil { // matched a unique full name gotocol.Message{gotocol.NameDrop, microservices[msg.Intention], time.Now(), gotocol.NilContext, msg.Intention}.GoSend(msg.ResponseChan) break } for n, ch := range microservices { // respond with all the online names that match the service component if names.Service(n) == msg.Intention { // if there was an update for the looked up service since last check // log.Printf("%v: matching %v with %v, last: %v metadata: %v\n", name, n, msg.Intention, lastrequest[callback{n, msg.ResponseChan}], metadata[n].registered) if metadata[n].registered.After(lastrequest[callback{n, msg.ResponseChan}]) { if metadata[n].online { gotocol.Message{gotocol.NameDrop, ch, time.Now(), gotocol.NilContext, n}.GoSend(msg.ResponseChan) } else { //log.Printf("%v:Forget %v\n", name, n) gotocol.Message{gotocol.Forget, ch, time.Now(), gotocol.NilContext, n}.GoSend(msg.ResponseChan) } } // remember for next time lastrequest[callback{n, msg.ResponseChan}] = msg.Sent } } case gotocol.Delete: // remove a node if microservices[msg.Intention] != nil { // matched a unique full name metadata[msg.Intention] = meta{false, time.Now()} // replicate request for _, c := range eurekaservices { gotocol.Message{gotocol.Replicate, nil, time.Now(), gotocol.NilContext, msg.Intention}.GoSend(c) } if edda.Logchan != nil { edda.Logchan <- msg } } case gotocol.Goodbye: gotocol.Message{gotocol.Goodbye, nil, time.Now(), gotocol.NilContext, name}.GoSend(msg.ResponseChan) log.Println(name + ": closing") return } } }
// Start staash, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { microservices := ribbon.MakeRouter() // outbound routes var caches, stores, volumes, cass, staash *ribbon.Router // subsets of the router dependencies := make(map[string]time.Time) // dependent service names and time last updated var parent chan gotocol.Message // remember how to talk back to creator requestor := make(map[string]gotocol.Routetype) // remember where requests came from when responding var name string // remember my name eureka := make(map[string]chan gotocol.Message, 1) // service registry hist := collect.NewHist("") ep, _ := time.ParseDuration(archaius.Conf.EurekaPoll) eurekaTicker := time.NewTicker(ep) for { select { case msg := <-listener: flow.Instrument(msg, name, hist) switch msg.Imposition { case gotocol.Hello: if name == "" { // if I don't have a name yet remember what I've been named parent = msg.ResponseChan // remember how to talk to my namer name = msg.Intention // message body is my name hist = collect.NewHist(name) } case gotocol.Inform: eureka[msg.Intention] = handlers.Inform(msg, name, listener) case gotocol.NameDrop: handlers.NameDrop(&dependencies, microservices, msg, name, listener, eureka, true) // true to setup cross zone routing caches = microservices.All(CachePkg) volumes = microservices.All(VolumePkg) stores = microservices.All(StorePkg) cass = microservices.All(PriamCassandraPkg) staash = microservices.All(StaashPkg) case gotocol.Forget: // forget a buddy handlers.Forget(&dependencies, microservices, msg) caches = microservices.All(CachePkg) volumes = microservices.All(VolumePkg) stores = microservices.All(StorePkg) cass = microservices.All(PriamCassandraPkg) staash = microservices.All(StaashPkg) case gotocol.GetRequest: // route the request on to a cache first if configured r := gotocol.PickRoute(requestor, msg) if caches.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, caches) r.State = cacheLookup } else { // route to any volumes if configured if volumes.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, volumes) r.State = volumeLookup } else { // route to any cassandra if configured if cass.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, cass) r.State = cassandraLookup } else { // route to stores if configured if stores.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, stores) r.State = storeLookup } else { // route to more staash layers if configured if staash.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, staash) r.State = staashLookup } } } } } //log.Printf("%v: %v route: %v", name, msg.Context, r) case gotocol.GetResponse: // return path from a request, resend or send payload back up using saved span context - server send r := gotocol.PickRoute(requestor, msg) if msg.Intention != "" { // we got a value, so pass it back up handlers.GetResponse(msg, name, listener, &requestor) } else { switch r.State { case cacheLookup: if volumes.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, volumes) r.State = volumeLookup break } fallthrough // no volumes so look for cassandra case volumeLookup: if cass.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, cass) r.State = cassandraLookup break } fallthrough // no cassandra so look for stores case cassandraLookup: if stores.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, stores) r.State = storeLookup break } fallthrough // no stores case storeLookup: if staash.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, staash) r.State = staashLookup break } fallthrough // no staash case staashLookup: // ran out of options to find anything so pass empty response back up handlers.GetResponse(msg, name, listener, &requestor) case newRequest: default: } } case gotocol.Put: // duplicate the request to any cache, volumes, stores, and cassandra but only to one of each type // storage class packages sideways Replicate if configured // to get a lossy write, configure multiple stores that don't cross replicate handlers.Put(msg, name, listener, &requestor, caches) handlers.Put(msg, name, listener, &requestor, cass) handlers.Put(msg, name, listener, &requestor, staash) handlers.Put(msg, name, listener, &requestor, stores) msg.Intention = names.Instance(name) + "/" + msg.Intention // store to an instance specific volume namespace handlers.Put(msg, name, listener, &requestor, volumes) case gotocol.Goodbye: for _, ch := range eureka { // tell name service I'm not going to be here ch <- gotocol.Message{gotocol.Delete, nil, time.Now(), gotocol.NilContext, name} } gotocol.Message{gotocol.Goodbye, nil, time.Now(), gotocol.NilContext, name}.GoSend(parent) return } case <-eurekaTicker.C: // check to see if any new dependencies have appeared for dep, _ := range dependencies { for _, ch := range eureka { ch <- gotocol.Message{gotocol.GetRequest, listener, time.Now(), gotocol.NilContext, dep} } } } } }
// Start the pirate, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { dunbar := 10 // starting point for how many buddies to remember // remember the channel to talk to named buddies buddies := make(map[string]chan gotocol.Message, dunbar) // remember who sent GoldCoin and how much, to buy favors benefactors := make(map[string]int, dunbar) var booty int // current GoldCoin balance var fsm chan gotocol.Message // remember how to talk back to creator var name string // remember my name var logger chan gotocol.Message // if set, send updates var chatrate time.Duration hist := collect.NewHist("") chatTicker := time.NewTicker(time.Hour) chatTicker.Stop() for { select { case msg := <-listener: collect.Measure(hist, time.Since(msg.Sent)) if archaius.Conf.Msglog { log.Printf("%v: %v\n", name, msg) } switch msg.Imposition { case gotocol.Hello: if name == "" { // if I don't have a name yet remember what I've been named fsm = msg.ResponseChan // remember how to talk to my namer name = msg.Intention // message body is my name hist = collect.NewHist(name) } case gotocol.Inform: // remember where to send updates logger = handlers.Inform(msg, name, listener) case gotocol.NameDrop: // don't remember too many buddies and don't talk to myself buddy := msg.Intention // message body is buddy name if len(buddies) < dunbar && buddy != name { // remember how to talk to this buddy buddies[buddy] = msg.ResponseChan // message channel is buddy's listener if logger != nil { // if it's setup, tell the logger I have a new buddy to talk to logger <- gotocol.Message{gotocol.Inform, listener, time.Now(), gotocol.NilContext, name + " " + buddy} } } case gotocol.Chat: // setup the ticker to run at the specified rate d, e := time.ParseDuration(msg.Intention) if e == nil && d >= time.Millisecond && d <= time.Hour { chatrate = d chatTicker = time.NewTicker(chatrate) // assume we got paid before we started chatting rand.Seed(int64(booty)) } case gotocol.GoldCoin: var coin int _, e := fmt.Sscanf(msg.Intention, "%d", &coin) if e == nil && coin > 0 { booty += coin for name, ch := range buddies { if ch == msg.ResponseChan { benefactors[name] += coin } } } case gotocol.Goodbye: if archaius.Conf.Msglog { log.Printf("%v: Going away with %v gold coins, chatting every %v\n", name, booty, chatrate) } gotocol.Message{gotocol.Goodbye, nil, time.Now(), gotocol.NilContext, name}.GoSend(fsm) return } case <-chatTicker.C: if rand.Intn(100) < 50 { // 50% of the time // use Namedrop to tell the last buddy about the first var firstBuddyName string var firstBuddyChan, lastBuddyChan chan gotocol.Message if len(buddies) >= 2 { for name, ch := range buddies { if firstBuddyName == "" { firstBuddyName = name firstBuddyChan = ch } else { lastBuddyChan = ch } gotocol.Message{gotocol.NameDrop, firstBuddyChan, time.Now(), gotocol.NewTrace(), firstBuddyName}.GoSend(lastBuddyChan) } } } else { // send a buddy some money if booty > 0 { donation := rand.Intn(booty) luckyNumber := rand.Intn(len(buddies)) if donation > 0 { for _, ch := range buddies { if luckyNumber == 0 { gotocol.Message{gotocol.GoldCoin, listener, time.Now(), gotocol.NewTrace(), fmt.Sprintf("%d", donation)}.GoSend(ch) booty -= donation break } else { luckyNumber-- } } } } } } } }
// Start edda, to listen for logging data from services func Start(name string) { // use a waitgroup so whoever starts edda can tell the logs have been flushed Wg.Add(1) defer Wg.Done() if Logchan == nil { return } var msg gotocol.Message microservices := make(map[string]bool, archaius.Conf.Dunbar) edges := make(map[string]bool, archaius.Conf.Dunbar) var ok bool hist := collect.NewHist(name) log.Println(name + ": starting") if archaius.Conf.GraphmlFile != "" { graphml.Setup(archaius.Conf.GraphmlFile) } if archaius.Conf.GraphjsonFile != "" { graphjson.Setup(archaius.Conf.GraphjsonFile) } if archaius.Conf.Neo4jURL != "" { graphneo4j.Setup(archaius.Conf.Neo4jURL) } for { msg, ok = <-Logchan collect.Measure(hist, time.Since(msg.Sent)) if !ok { break // channel was closed } if archaius.Conf.Msglog { log.Printf("%v(backlog %v): %v\n", name, len(Logchan), msg) } switch msg.Imposition { case gotocol.Inform: edge := names.FilterEdge(msg.Intention) if edges[edge] == false { // only log an edge once edges[edge] = true graphml.WriteEdge(edge) graphjson.WriteEdge(edge, msg.Sent) graphneo4j.WriteEdge(strings.Replace(msg.Intention, "-", "_", -1), msg.Sent) } case gotocol.Put: node := names.FilterNode(msg.Intention) if microservices[node] == false { // only log a node once microservices[node] = true graphml.WriteNode(node + " " + names.Package(msg.Intention)) graphjson.WriteNode(node+" "+names.Package(msg.Intention), msg.Sent) graphneo4j.WriteNode(strings.Replace(msg.Intention, "-", "_", -1)+" "+names.Package(msg.Intention), msg.Sent) } case gotocol.Forget: // forget the edge // problem here in that edges may be reported multiple times from several sources // however after filtering all matching edges are reported as forgotten when the first is // need to maintain the full model and a filtered model with counts edge := names.FilterEdge(msg.Intention) if edges[edge] == true { // only remove an edge once edges[edge] = false graphjson.WriteForget(edge, msg.Sent) } case gotocol.Delete: // remove the node node := names.FilterNode(msg.Intention) if microservices[node] == true { // only remove nodes that exist, and only log it once microservices[node] = false graphjson.WriteDone(node, msg.Sent) } } } log.Println(name + ": closing") graphml.Close() graphjson.Close() graphneo4j.Close() }
// Start store, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { // remember the channel to talk to microservices microservices := ribbon.MakeRouter() dependencies := make(map[string]time.Time) // dependent services and time last updated store := make(map[string]string, 4) // key value store store["why?"] = "because..." var netflixoss chan gotocol.Message // remember creator and how to talk back to incoming requests var name string // remember my name eureka := make(map[string]chan gotocol.Message, len(archaius.Conf.ZoneNames)) // service registry per zone hist := collect.NewHist("") ep, _ := time.ParseDuration(archaius.Conf.EurekaPoll) eurekaTicker := time.NewTicker(ep) for { select { case msg := <-listener: flow.Instrument(msg, name, hist) switch msg.Imposition { case gotocol.Hello: if name == "" { // if I don't have a name yet remember what I've been named netflixoss = msg.ResponseChan // remember how to talk to my namer name = msg.Intention // message body is my name hist = collect.NewHist(name) } case gotocol.Inform: eureka[msg.Intention] = handlers.Inform(msg, name, listener) case gotocol.NameDrop: // cross zone = true handlers.NameDrop(&dependencies, microservices, msg, name, listener, eureka, true) case gotocol.Forget: // forget a buddy handlers.Forget(&dependencies, microservices, msg) case gotocol.GetRequest: // return any stored value for this key outmsg := gotocol.Message{gotocol.GetResponse, listener, time.Now(), msg.Ctx, store[msg.Intention]} flow.AnnotateSend(outmsg, name) outmsg.GoSend(msg.ResponseChan) case gotocol.GetResponse: // return path from a request, send payload back up (not currently used) case gotocol.Put: // set a key value pair and replicate to other stores var key, value string fmt.Sscanf(msg.Intention, "%s%s", &key, &value) if key != "" && value != "" { store[key] = value // duplicate the request on to all connected store nodes with the same package name as this one for _, n := range microservices.All(names.Package(name)).Names() { outmsg := gotocol.Message{gotocol.Replicate, listener, time.Now(), msg.Ctx.NewParent(), msg.Intention} flow.AnnotateSend(outmsg, name) outmsg.GoSend(microservices.Named(n)) } } case gotocol.Replicate: // Replicate is used between store nodes // end point for a request var key, value string fmt.Sscanf(msg.Intention, "%s%s", &key, &value) // log.Printf("store: %v:%v", key, value) if key != "" && value != "" { store[key] = value } case gotocol.Goodbye: gotocol.Message{gotocol.Goodbye, nil, time.Now(), gotocol.NilContext, name}.GoSend(netflixoss) return } case <-eurekaTicker.C: // check to see if any new dependencies have appeared for dep, _ := range dependencies { for _, ch := range eureka { ch <- gotocol.Message{gotocol.GetRequest, listener, time.Now(), gotocol.NilContext, dep} } } } } }
// Start priamCassandra, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { // remember the channel to talk to microservices microservices := ribbon.MakeRouter() // track the hash values owned by each node in the ring var ring ByToken dependencies := make(map[string]time.Time) // dependent services and time last updated store := make(map[string]string, 4) // key value store store["why?"] = "because..." var parent chan gotocol.Message // remember how to talk back to creator var name string // remember my name eureka := make(map[string]chan gotocol.Message, len(archaius.Conf.ZoneNames)*archaius.Conf.Regions) // service registry per zone and region hist := collect.NewHist("") ep, _ := time.ParseDuration(archaius.Conf.EurekaPoll) eurekaTicker := time.NewTicker(ep) for { select { case msg := <-listener: flow.Instrument(msg, name, hist) switch msg.Imposition { case gotocol.Hello: if name == "" { // if I don't have a name yet remember what I've been named parent = msg.ResponseChan // remember how to talk to my namer name = msg.Intention // message body is my name hist = collect.NewHist(name) } case gotocol.Inform: eureka[msg.Intention] = handlers.Inform(msg, name, listener) case gotocol.NameDrop: // cross zone = true handlers.NameDrop(&dependencies, microservices, msg, name, listener, eureka, true) case gotocol.Forget: // forget a buddy handlers.Forget(&dependencies, microservices, msg) case gotocol.Chat: // Gossip setup notification of hash values for nodes, cass1:123,cass2:456 ring = RingConfig(msg.Intention) case gotocol.GetRequest: // see if the data is stored on this node i := ring.Find(ringHash(msg.Intention)) //log.Printf("%v: %v %v\n", name, i, ringHash(msg.Intention)) if len(ring) == 0 || ring[i].name == name { // ring is setup so only respond if this is the right place // return any stored value for this key (Cassandra READ.ONE behavior) outmsg := gotocol.Message{gotocol.GetResponse, listener, time.Now(), msg.Ctx, store[msg.Intention]} flow.AnnotateSend(outmsg, name) outmsg.GoSend(msg.ResponseChan) } else { // forward the message to the right place, but don't change the ResponseChan or span outmsg := gotocol.Message{gotocol.GetRequest, msg.ResponseChan, time.Now(), msg.Ctx.AddSpan(), msg.Intention} flow.AnnotateSend(outmsg, name) outmsg.GoSend(microservices.Named(ring[i].name)) } case gotocol.GetResponse: // return path from a request, send payload back up, not used by priamCassandra currently case gotocol.Put: // set a key value pair and replicate globally var key, value string fmt.Sscanf(msg.Intention, "%s%s", &key, &value) if key != "" && value != "" { i := ring.Find(ringHash(key)) if len(ring) == 0 || ring[i].name == name { // ring is setup so only store if this is the right place store[key] = value } else { // forward the message to the right place, but don't change the ResponseChan or context parent outmsg := gotocol.Message{gotocol.Put, msg.ResponseChan, time.Now(), msg.Ctx.AddSpan(), msg.Intention} flow.AnnotateSend(outmsg, name) outmsg.GoSend(microservices.Named(ring[i].name)) } // duplicate the request on to priamCassandra nodes in each zone and one in each region for _, z := range names.OtherZones(name, archaius.Conf.ZoneNames) { // replicate request for _, n := range microservices.Names() { if names.Region(n) == names.Region(name) && names.Zone(n) == z { outmsg := gotocol.Message{gotocol.Replicate, listener, time.Now(), msg.Ctx.NewParent(), msg.Intention} flow.AnnotateSend(outmsg, name) outmsg.GoSend(microservices.Named(n)) break // only need to send it to one node in each zone } } } for _, r := range names.OtherRegions(name, archaius.Conf.RegionNames[0:archaius.Conf.Regions]) { for _, n := range microservices.Names() { if names.Region(n) == r { outmsg := gotocol.Message{gotocol.Replicate, listener, time.Now(), msg.Ctx.NewParent(), msg.Intention} flow.AnnotateSend(outmsg, name) outmsg.GoSend(microservices.Named(n)) break // only need to send it to one node in each region } } } } case gotocol.Replicate: // Replicate is only used between priamCassandra nodes // end point for a request var key, value string fmt.Sscanf(msg.Intention, "%s%s", &key, &value) // log.Printf("priamCassandra: %v:%v", key, value) if key != "" && value != "" { i := ring.Find(ringHash(key)) if len(ring) == 0 || ring[i].name == name { // ring is setup so only store if this is the right place store[key] = value } else { // forward the message to the right place, but don't change the ResponseChan outmsg := gotocol.Message{gotocol.Replicate, msg.ResponseChan, time.Now(), msg.Ctx, msg.Intention} flow.AnnotateSend(outmsg, name) outmsg.GoSend(microservices.Named(ring[i].name)) } } // name looks like: netflixoss.us-east-1.zoneC.cassTurtle.priamCassandra.cassTurtle11 myregion := names.Region(name) //log.Printf("%v: %v\n", name, myregion) // find if this was a cross region Replicate // find the name matching incoming request channel to see where its coming from in := microservices.NameChan(msg.ResponseChan) if in != "" && myregion != names.Region(in) { // Replicate from out of region needs to be Replicated once only to other zones in this Region for _, z := range names.OtherZones(name, archaius.Conf.ZoneNames) { // replicate request for _, n := range microservices.Names() { if names.Region(n) == myregion && names.Zone(n) == z { outmsg := gotocol.Message{gotocol.Replicate, listener, time.Now(), msg.Ctx.NewParent(), msg.Intention} flow.AnnotateSend(outmsg, name) outmsg.GoSend(microservices.Named(n)) break // only need to send it to one node in each zone } } } break } case gotocol.Goodbye: gotocol.Message{gotocol.Goodbye, nil, time.Now(), gotocol.NilContext, name}.GoSend(parent) return } case <-eurekaTicker.C: // check to see if any new dependencies have appeared for dep, _ := range dependencies { for _, ch := range eureka { ch <- gotocol.Message{gotocol.GetRequest, listener, time.Now(), gotocol.NilContext, dep} } } } } }