// SubscribeLogs creates a log subscription and streams back logs func (lb *LogBroker) SubscribeLogs(request *api.SubscribeLogsRequest, stream api.Logs_SubscribeLogsServer) error { ctx := stream.Context() if err := validateSelector(request.Selector); err != nil { return err } subscription := lb.newSubscription(request.Selector, request.Options) subscription.Run(lb.pctx) defer subscription.Stop() log := log.G(ctx).WithFields( logrus.Fields{ "method": "(*LogBroker).SubscribeLogs", "subscription.id": subscription.message.ID, }, ) log.Debug("subscribed") publishCh, publishCancel := lb.subscribe(subscription.message.ID) defer publishCancel() lb.registerSubscription(subscription) defer lb.unregisterSubscription(subscription) completed := subscription.Wait(ctx) for { select { case <-ctx.Done(): return ctx.Err() case <-lb.pctx.Done(): return lb.pctx.Err() case event := <-publishCh: publish := event.(*logMessage) if publish.completed { return publish.err } if err := stream.Send(&api.SubscribeLogsMessage{ Messages: publish.Messages, }); err != nil { return err } case <-completed: completed = nil lb.logQueue.Publish(&logMessage{ PublishLogsMessage: &api.PublishLogsMessage{ SubscriptionID: subscription.message.ID, }, completed: true, err: subscription.Err(), }) } } }
// ListenSubscriptions returns a stream of matching subscriptions for the current node func (lb *LogBroker) ListenSubscriptions(request *api.ListenSubscriptionsRequest, stream api.LogBroker_ListenSubscriptionsServer) error { remote, err := ca.RemoteNode(stream.Context()) if err != nil { return err } log := log.G(stream.Context()).WithFields( logrus.Fields{ "method": "(*LogBroker).ListenSubscriptions", "node": remote.NodeID, }, ) subscriptions, subscriptionCh, subscriptionCancel := lb.watchSubscriptions() defer subscriptionCancel() log.Debug("node registered") // Start by sending down all active subscriptions. for _, subscription := range subscriptions { select { case <-stream.Context().Done(): return stream.Context().Err() case <-lb.pctx.Done(): return nil default: } if err := stream.Send(subscription); err != nil { log.Error(err) return err } } // Send down new subscriptions. // TODO(aluzzardi): We should filter by relevant tasks for this node rather for { select { case v := <-subscriptionCh: subscription := v.(*api.SubscriptionMessage) if err := stream.Send(subscription); err != nil { log.Error(err) return err } case <-stream.Context().Done(): return stream.Context().Err() case <-lb.pctx.Done(): return nil } } }
// SubscribeLogs creates a log subscription and streams back logs func (lb *LogBroker) SubscribeLogs(request *api.SubscribeLogsRequest, stream api.Logs_SubscribeLogsServer) error { ctx := stream.Context() if err := validateSelector(request.Selector); err != nil { return err } subscription := &api.SubscriptionMessage{ ID: identity.NewID(), Selector: request.Selector, Options: request.Options, } log := log.G(ctx).WithFields( logrus.Fields{ "method": "(*LogBroker).SubscribeLogs", "subscription.id": subscription.ID, }, ) log.Debug("subscribed") publishCh, publishCancel := lb.subscribe(subscription.ID) defer publishCancel() lb.registerSubscription(subscription) defer lb.unregisterSubscription(subscription) for { select { case event := <-publishCh: publish := event.(*api.PublishLogsMessage) if err := stream.Send(&api.SubscribeLogsMessage{ Messages: publish.Messages, }); err != nil { return err } case <-ctx.Done(): return ctx.Err() case <-lb.pctx.Done(): return nil } } }
// Join asks to a member of the raft to propose // a configuration change and add us as a member thus // beginning the log replication process. This method // is called from an aspiring member to an existing member func (n *Node) Join(ctx context.Context, req *api.JoinRequest) (*api.JoinResponse, error) { nodeInfo, err := ca.RemoteNode(ctx) if err != nil { return nil, err } fields := logrus.Fields{ "node.id": nodeInfo.NodeID, "method": "(*Node).Join", "raft_id": fmt.Sprintf("%x", n.Config.ID), } if nodeInfo.ForwardedBy != nil { fields["forwarder.id"] = nodeInfo.ForwardedBy.NodeID } log := log.G(ctx).WithFields(fields) log.Debug("") // can't stop the raft node while an async RPC is in progress n.stopMu.RLock() defer n.stopMu.RUnlock() n.membershipLock.Lock() defer n.membershipLock.Unlock() if !n.IsMember() { return nil, ErrNoRaftMember } if !n.isLeader() { return nil, ErrLostLeadership } // Find a unique ID for the joining member. var raftID uint64 for { raftID = uint64(rand.Int63()) + 1 if n.cluster.GetMember(raftID) == nil && !n.cluster.IsIDRemoved(raftID) { break } } remoteAddr := req.Addr // If the joining node sent an address like 0.0.0.0:4242, automatically // determine its actual address based on the GRPC connection. This // avoids the need for a prospective member to know its own address. requestHost, requestPort, err := net.SplitHostPort(remoteAddr) if err != nil { return nil, fmt.Errorf("invalid address %s in raft join request", remoteAddr) } requestIP := net.ParseIP(requestHost) if requestIP != nil && requestIP.IsUnspecified() { remoteHost, _, err := net.SplitHostPort(nodeInfo.RemoteAddr) if err != nil { return nil, err } remoteAddr = net.JoinHostPort(remoteHost, requestPort) } // We do not bother submitting a configuration change for the // new member if we can't contact it back using its address if err := n.checkHealth(ctx, remoteAddr, 5*time.Second); err != nil { return nil, err } err = n.addMember(ctx, remoteAddr, raftID, nodeInfo.NodeID) if err != nil { log.WithError(err).Errorf("failed to add member %x", raftID) return nil, err } var nodes []*api.RaftMember for _, node := range n.cluster.Members() { nodes = append(nodes, &api.RaftMember{ RaftID: node.RaftID, NodeID: node.NodeID, Addr: node.Addr, }) } log.Debugf("node joined") return &api.JoinResponse{Members: nodes, RaftID: raftID}, nil }
// ListenSubscriptions returns a stream of matching subscriptions for the current node func (lb *LogBroker) ListenSubscriptions(request *api.ListenSubscriptionsRequest, stream api.LogBroker_ListenSubscriptionsServer) error { remote, err := ca.RemoteNode(stream.Context()) if err != nil { return err } lb.nodeConnected(remote.NodeID) defer lb.nodeDisconnected(remote.NodeID) log := log.G(stream.Context()).WithFields( logrus.Fields{ "method": "(*LogBroker).ListenSubscriptions", "node": remote.NodeID, }, ) subscriptions, subscriptionCh, subscriptionCancel := lb.watchSubscriptions(remote.NodeID) defer subscriptionCancel() log.Debug("node registered") activeSubscriptions := make(map[string]*subscription) defer func() { // If the worker quits, mark all active subscriptions as finished. for _, subscription := range activeSubscriptions { subscription.Done(remote.NodeID, fmt.Errorf("node %s disconnected unexpectedly", remote.NodeID)) } }() // Start by sending down all active subscriptions. for _, subscription := range subscriptions { select { case <-stream.Context().Done(): return stream.Context().Err() case <-lb.pctx.Done(): return nil default: } if err := stream.Send(subscription.message); err != nil { log.Error(err) return err } activeSubscriptions[subscription.message.ID] = subscription } // Send down new subscriptions. for { select { case v := <-subscriptionCh: subscription := v.(*subscription) if subscription.message.Close { log.WithField("subscription.id", subscription.message.ID).Debug("subscription closed") delete(activeSubscriptions, subscription.message.ID) } else { // Avoid sending down the same subscription multiple times if _, ok := activeSubscriptions[subscription.message.ID]; ok { continue } activeSubscriptions[subscription.message.ID] = subscription log.WithField("subscription.id", subscription.message.ID).Debug("subscription added") } if err := stream.Send(subscription.message); err != nil { log.Error(err) return err } case <-stream.Context().Done(): return stream.Context().Err() case <-lb.pctx.Done(): return nil } } }