...

Source file src/github.com/launchdarkly/go-sdk-events/v2/event_processor_benchmark_test.go

Documentation: github.com/launchdarkly/go-sdk-events/v2

     1  package ldevents
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"testing"
     7  
     8  	"github.com/launchdarkly/go-sdk-common/v3/ldcontext"
     9  	"github.com/launchdarkly/go-sdk-common/v3/ldtime"
    10  	"github.com/launchdarkly/go-sdk-common/v3/lduser"
    11  	"github.com/launchdarkly/go-sdk-common/v3/ldvalue"
    12  )
    13  
    14  const benchmarkEventCount = 100
    15  
    16  // Timings for BenchmarkEventProcessor are likely to vary a lot, because it is testing the full pipeline of
    17  // event processing from EventProcessor.Send() until the payload is handed off to the EventSender, which
    18  // takes place across multiple goroutines. We are mostly concerned with allocations, and general trends in
    19  // execution time.
    20  
    21  func BenchmarkEventProcessor(b *testing.B) {
    22  	configDefault := EventsConfiguration{Capacity: 1000}
    23  
    24  	doEvents := func(b *testing.B, config EventsConfiguration, sendEvents func(EventProcessor)) {
    25  		ep, es := createBenchmarkEventProcessorAndSender(config)
    26  		defer ep.Close()
    27  
    28  		b.ResetTimer()
    29  
    30  		for i := 0; i < b.N; i++ {
    31  			sendEvents(ep)
    32  			ep.Flush()
    33  			es.awaitPayload()
    34  		}
    35  
    36  		b.StopTimer() // so ep.Close() isn't included in the time
    37  	}
    38  
    39  	b.Run("summarize feature events", func(b *testing.B) {
    40  		doEvents(b, configDefault, sendBenchmarkFeatureEvents(false))
    41  	})
    42  
    43  	b.Run("feature events with full tracking", func(b *testing.B) {
    44  		doEvents(b, configDefault, sendBenchmarkFeatureEvents(true))
    45  	})
    46  
    47  	b.Run("custom events", func(b *testing.B) {
    48  		doEvents(b, configDefault, sendBenchmarkCustomEvents())
    49  	})
    50  }
    51  
    52  func makeBenchmarkUsers() []ldcontext.Context {
    53  	numUsers := 10
    54  	ret := make([]ldcontext.Context, 0, numUsers)
    55  	for i := 0; i < numUsers; i++ {
    56  		user := lduser.NewUserBuilder(fmt.Sprintf("user%d", i)).
    57  			Name(fmt.Sprintf("name%d", i)).
    58  			Build()
    59  		ret = append(ret, user)
    60  	}
    61  	return ret
    62  }
    63  
    64  func sendBenchmarkFeatureEvents(tracking bool) func(EventProcessor) {
    65  	events := make([]EvaluationData, 0, benchmarkEventCount)
    66  	users := makeBenchmarkUsers()
    67  	flagCount := 10
    68  	flagVersions := 3
    69  	flagVariations := 2
    70  	rnd := rand.New(rand.NewSource(int64(ldtime.UnixMillisNow())))
    71  
    72  	for i := 0; i < benchmarkEventCount; i++ {
    73  		variation := rnd.Intn(flagVariations)
    74  		event := EvaluationData{
    75  			BaseEvent: BaseEvent{
    76  				Context:      Context(users[rnd.Intn(len(users))]),
    77  				CreationDate: ldtime.UnixMillisNow(),
    78  			},
    79  			Key:              fmt.Sprintf("flag%d", rnd.Intn(flagCount)),
    80  			Version:          ldvalue.NewOptionalInt(rnd.Intn(flagVersions) + 1),
    81  			Variation:        ldvalue.NewOptionalInt(variation),
    82  			Value:            ldvalue.Int(variation),
    83  			RequireFullEvent: tracking,
    84  		}
    85  		events = append(events, event)
    86  	}
    87  
    88  	return func(ep EventProcessor) {
    89  		for _, e := range events {
    90  			ep.RecordEvaluation(e)
    91  		}
    92  	}
    93  }
    94  
    95  func sendBenchmarkCustomEvents() func(EventProcessor) {
    96  	events := make([]CustomEventData, 0, benchmarkEventCount)
    97  	users := makeBenchmarkUsers()
    98  	keyCount := 5
    99  	rnd := rand.New(rand.NewSource(int64(ldtime.UnixMillisNow())))
   100  
   101  	for i := 0; i < benchmarkEventCount; i++ {
   102  		event := CustomEventData{
   103  			BaseEvent: BaseEvent{
   104  				Context:      Context(users[rnd.Intn(len(users))]),
   105  				CreationDate: ldtime.UnixMillisNow(),
   106  			},
   107  			Key:  fmt.Sprintf("event%d", rnd.Intn(keyCount)),
   108  			Data: ldvalue.String("data"),
   109  		}
   110  		events = append(events, event)
   111  	}
   112  
   113  	return func(ep EventProcessor) {
   114  		for _, e := range events {
   115  			ep.RecordCustomEvent(e)
   116  		}
   117  	}
   118  }
   119  
   120  // This is  simpler than the mockEventSender used in other tests, because we don't need to parse the event
   121  // payload - we just want to know when it's been sent - and we don't need to simulate different results.
   122  type benchmarkMockEventSender struct {
   123  	payloadCh chan []byte
   124  }
   125  
   126  func newBenchmarkMockEventSender() *benchmarkMockEventSender {
   127  	return &benchmarkMockEventSender{
   128  		payloadCh: make(chan []byte, 100),
   129  	}
   130  }
   131  
   132  func (ms *benchmarkMockEventSender) SendEventData(
   133  	kind EventDataKind,
   134  	data []byte,
   135  	eventCount int,
   136  ) EventSenderResult {
   137  	ms.payloadCh <- data
   138  	return EventSenderResult{Success: true}
   139  }
   140  
   141  func (ms *benchmarkMockEventSender) awaitPayload() {
   142  	<-ms.payloadCh
   143  }
   144  
   145  func createBenchmarkEventProcessorAndSender(config EventsConfiguration) (EventProcessor, *benchmarkMockEventSender) {
   146  	sender := newBenchmarkMockEventSender()
   147  	config.EventSender = sender
   148  	return NewDefaultEventProcessor(config), sender
   149  }
   150  

View as plain text