code.vegaprotocol.io/vega@v0.79.0/datanode/api/event_observer.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package api
    17  
    18  import (
    19  	"context"
    20  	"time"
    21  
    22  	"code.vegaprotocol.io/vega/core/events"
    23  	"code.vegaprotocol.io/vega/datanode/metrics"
    24  	"code.vegaprotocol.io/vega/libs/subscribers"
    25  	"code.vegaprotocol.io/vega/logging"
    26  	protoapi "code.vegaprotocol.io/vega/protos/vega/api/v1"
    27  	eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1"
    28  )
    29  
    30  type eventBusServer interface {
    31  	RecvMsg(m interface{}) error
    32  	Context() context.Context
    33  	Send(data []*eventspb.BusEvent) error
    34  }
    35  
    36  type eventObserver struct {
    37  	log          *logging.Logger
    38  	eventService EventService
    39  	Config       Config
    40  }
    41  
    42  type coreServiceEventBusServer struct {
    43  	stream protoapi.CoreService_ObserveEventBusServer
    44  }
    45  
    46  func (t coreServiceEventBusServer) RecvMsg(m interface{}) error {
    47  	return t.stream.RecvMsg(m)
    48  }
    49  
    50  func (t coreServiceEventBusServer) Context() context.Context {
    51  	return t.stream.Context()
    52  }
    53  
    54  func (t coreServiceEventBusServer) Send(data []*eventspb.BusEvent) error {
    55  	resp := &protoapi.ObserveEventBusResponse{
    56  		Events: data,
    57  	}
    58  	return t.stream.Send(resp)
    59  }
    60  
    61  func (e *eventObserver) ObserveEventBus(
    62  	stream protoapi.CoreService_ObserveEventBusServer,
    63  ) error {
    64  	return observeEventBus(e.log, e.Config, coreServiceEventBusServer{stream}, e.eventService)
    65  }
    66  
    67  func observeEventBus(log *logging.Logger, config Config, eventBusServer eventBusServer, eventService EventService) error {
    68  	defer metrics.StartActiveEventBusConnection()()
    69  
    70  	ctx, cfunc := context.WithCancel(eventBusServer.Context())
    71  	defer cfunc()
    72  
    73  	// now we start listening for a few seconds in order to get at least the very first message
    74  	// this will be blocking until the connection by the client is closed
    75  	// and we will not start processing any events until we receive the original request
    76  	// indicating filters and batch size.
    77  	req, err := recvEventRequest(ctx, defaultReqTimeout, eventBusServer)
    78  	if err != nil {
    79  		log.Error("Error receiving event request", logging.Error(err))
    80  		// client exited, nothing to do
    81  		return nil //nolint:nilerr
    82  	}
    83  
    84  	// now we will aggregate filter out of the initial request
    85  	types, err := events.ProtoToInternal(req.Type...)
    86  	if err != nil {
    87  		return formatE(ErrMalformedRequest, err)
    88  	}
    89  
    90  	metrics.StartEventBusActiveSubscriptionCount(types)
    91  	defer metrics.StopEventBusActiveSubscriptionCount(types)
    92  
    93  	var filters []subscribers.EventFilter
    94  	if len(req.MarketId) > 0 && len(req.PartyId) > 0 {
    95  		filters = append(filters, events.GetPartyAndMarketFilter(req.MarketId, req.PartyId))
    96  	} else {
    97  		if len(req.MarketId) > 0 {
    98  			filters = append(filters, events.GetMarketIDFilter(req.MarketId))
    99  		}
   100  		if len(req.PartyId) > 0 {
   101  			filters = append(filters, events.GetPartyIDFilter(req.PartyId))
   102  		}
   103  	}
   104  
   105  	// number of retries to -1 to have pretty much unlimited retries
   106  	ch, bCh := eventService.ObserveEvents(ctx, config.StreamRetries, types, int(req.BatchSize), filters...)
   107  	defer close(bCh)
   108  
   109  	if req.BatchSize > 0 {
   110  		return observeEventsWithAck(ctx, log, eventBusServer, req.BatchSize, ch, bCh)
   111  	}
   112  
   113  	return observeEvents(ctx, log, eventBusServer, ch)
   114  }
   115  
   116  func observeEvents(
   117  	ctx context.Context,
   118  	log *logging.Logger,
   119  	stream eventBusServer,
   120  	ch <-chan []*eventspb.BusEvent,
   121  ) error {
   122  	sentEventStatTicker := time.NewTicker(time.Second)
   123  	defer sentEventStatTicker.Stop()
   124  	publishedEvents := eventStats{}
   125  
   126  	for {
   127  		select {
   128  		case <-sentEventStatTicker.C:
   129  			publishedEvents.publishStats()
   130  			publishedEvents = eventStats{}
   131  		case data, ok := <-ch:
   132  			if !ok {
   133  				return nil
   134  			}
   135  
   136  			if err := stream.Send(data); err != nil {
   137  				log.Error("Error sending event on stream", logging.Error(err))
   138  				return formatE(ErrStreamInternal, err)
   139  			}
   140  			publishedEvents.updateStats(data)
   141  		case <-ctx.Done():
   142  			return formatE(ErrStreamInternal, ctx.Err())
   143  		}
   144  	}
   145  }
   146  
   147  func observeEventsWithAck(
   148  	ctx context.Context,
   149  	log *logging.Logger,
   150  	stream eventBusServer,
   151  	batchSize int64,
   152  	ch <-chan []*eventspb.BusEvent,
   153  	bCh chan<- int,
   154  ) error {
   155  	sentEventStatTicker := time.NewTicker(time.Second)
   156  	defer sentEventStatTicker.Stop()
   157  	publishedEvents := eventStats{}
   158  
   159  	for {
   160  		select {
   161  		case <-sentEventStatTicker.C:
   162  			publishedEvents.publishStats()
   163  			publishedEvents = eventStats{}
   164  		case data, ok := <-ch:
   165  			if !ok {
   166  				return nil
   167  			}
   168  
   169  			if err := stream.Send(data); err != nil {
   170  				log.Error("Error sending event on stream", logging.Error(err))
   171  				return formatE(ErrStreamInternal, err)
   172  			}
   173  			publishedEvents.updateStats(data)
   174  		case <-ctx.Done():
   175  			return formatE(ErrStreamInternal, ctx.Err())
   176  		}
   177  
   178  		// now we try to read again the new size / ack
   179  		req, err := recvEventRequest(ctx, defaultReqTimeout, stream)
   180  		if err != nil {
   181  			return formatE(ErrStreamInternal, err)
   182  		}
   183  
   184  		if req.BatchSize != batchSize {
   185  			batchSize = req.BatchSize
   186  			bCh <- int(batchSize)
   187  		}
   188  	}
   189  }
   190  
   191  const defaultReqTimeout = time.Second * 5
   192  
   193  func recvEventRequest(ctx context.Context, timeout time.Duration, stream eventBusServer) (*protoapi.ObserveEventBusRequest, error) {
   194  	type resp struct {
   195  		nb  *protoapi.ObserveEventBusRequest
   196  		err error
   197  	}
   198  
   199  	oebCh := make(chan resp, 1)
   200  
   201  	go func() {
   202  		defer close(oebCh)
   203  
   204  		nb := &protoapi.ObserveEventBusRequest{}
   205  		err := stream.RecvMsg(nb)
   206  
   207  		oebCh <- resp{nb: nb, err: err}
   208  	}()
   209  
   210  	ctx, cancel := context.WithTimeout(ctx, timeout)
   211  	defer cancel()
   212  
   213  	select {
   214  	case rsp := <-oebCh:
   215  		return rsp.nb, rsp.err
   216  	case <-ctx.Done():
   217  		return nil, ctx.Err()
   218  	}
   219  }
   220  
   221  // this needs to be greater than the highest eventspb.BusEvent event type.
   222  const maxEventTypeOrdinal = 299
   223  
   224  type eventStats struct {
   225  	eventCount [maxEventTypeOrdinal + 1]int
   226  }
   227  
   228  func (s *eventStats) updateStats(events []*eventspb.BusEvent) {
   229  	for _, event := range events {
   230  		eventType := event.Type
   231  		if int(eventType) > maxEventTypeOrdinal {
   232  			eventType = maxEventTypeOrdinal
   233  		}
   234  		s.eventCount[eventType] = s.eventCount[eventType] + 1
   235  	}
   236  }
   237  
   238  func (s eventStats) publishStats() {
   239  	for idx, count := range s.eventCount {
   240  		if count > 0 {
   241  			if idx == maxEventTypeOrdinal {
   242  				metrics.EventBusPublishedEventsAdd("Unknown", float64(count))
   243  			}
   244  
   245  			eventName := eventspb.BusEventType_name[int32(idx)]
   246  			metrics.EventBusPublishedEventsAdd(eventName, float64(count))
   247  		}
   248  	}
   249  }