diff --git a/plugin/storage/es/spanstore/writer.go b/plugin/storage/es/spanstore/writer.go index 74be940a308..a0a8d77cd92 100644 --- a/plugin/storage/es/spanstore/writer.go +++ b/plugin/storage/es/spanstore/writer.go @@ -30,8 +30,10 @@ import ( ) const ( - spanType = "span" - serviceType = "service" + spanType = "span" + serviceType = "service" + serviceCacheTTLDefault = 12 * time.Hour + indexCacheTTLDefault = 48 * time.Hour ) type spanWriterMetrics struct { @@ -63,12 +65,23 @@ type SpanWriterParams struct { TagDotReplacement string Archive bool UseReadWriteAliases bool + ServiceCacheTTL time.Duration + IndexCacheTTL time.Duration } // NewSpanWriter creates a new SpanWriter for use func NewSpanWriter(p SpanWriterParams) *SpanWriter { - // TODO: Configurable TTL - serviceOperationStorage := NewServiceOperationStorage(p.Client, p.Logger, time.Hour*12) + serviceCacheTTL := p.ServiceCacheTTL + if p.ServiceCacheTTL == 0 { + serviceCacheTTL = serviceCacheTTLDefault + } + + indexCacheTTL := p.IndexCacheTTL + if p.ServiceCacheTTL == 0 { + indexCacheTTL = indexCacheTTLDefault + } + + serviceOperationStorage := NewServiceOperationStorage(p.Client, p.Logger, serviceCacheTTL) return &SpanWriter{ client: p.Client, logger: p.Logger, @@ -79,7 +92,7 @@ func NewSpanWriter(p SpanWriterParams) *SpanWriter { indexCache: cache.NewLRUWithOptions( 5, &cache.Options{ - TTL: 48 * time.Hour, + TTL: indexCacheTTL, }, ), spanConverter: dbmodel.NewFromDomain(p.AllTagsAsFields, p.TagKeysAsFields, p.TagDotReplacement), diff --git a/plugin/storage/es/spanstore/writer_test.go b/plugin/storage/es/spanstore/writer_test.go index 1ac8070c5aa..b92cc98a0f8 100644 --- a/plugin/storage/es/spanstore/writer_test.go +++ b/plugin/storage/es/spanstore/writer_test.go @@ -356,6 +356,74 @@ func TestNewSpanTags(t *testing.T) { } } +func TestSpanWriterParamsTTL(t *testing.T) { + logger, _ := testutils.NewLogger() + metricsFactory := metricstest.NewFactory(0) + testCases := []struct { + indexTTL time.Duration + serviceTTL time.Duration + name string + expectedAddCalls int + }{ + { + indexTTL: 0, + serviceTTL: 0, + name: "uses defaults", + expectedAddCalls: 1, + }, + { + indexTTL: 1 * time.Nanosecond, + serviceTTL: 1 * time.Nanosecond, + name: "uses provided values", + expectedAddCalls: 3, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + client := &mocks.Client{} + params := SpanWriterParams{ + Client: client, + Logger: logger, + MetricsFactory: metricsFactory, + ServiceCacheTTL: test.serviceTTL, + IndexCacheTTL: test.indexTTL, + } + w := NewSpanWriter(params) + + svc := dbmodel.Service{ + ServiceName: "foo", + OperationName: "bar", + } + serviceHash := hashCode(svc) + + serviceIndexName := "jaeger-service-1995-04-21" + + indexService := &mocks.IndexService{} + + indexService.On("Index", stringMatcher(serviceIndexName)).Return(indexService) + indexService.On("Type", stringMatcher(serviceType)).Return(indexService) + indexService.On("Id", stringMatcher(serviceHash)).Return(indexService) + indexService.On("BodyJson", mock.AnythingOfType("dbmodel.Service")).Return(indexService) + indexService.On("Add") + + client.On("Index").Return(indexService) + + jsonSpan := &dbmodel.Span{ + Process: dbmodel.Process{ServiceName: "foo"}, + OperationName: "bar", + } + + w.writeService(serviceIndexName, jsonSpan) + time.Sleep(1 * time.Nanosecond) + w.writeService(serviceIndexName, jsonSpan) + time.Sleep(1 * time.Nanosecond) + w.writeService(serviceIndexName, jsonSpan) + indexService.AssertNumberOfCalls(t, "Add", test.expectedAddCalls) + }) + } +} + // stringMatcher can match a string argument when it contains a specific substring q func stringMatcher(q string) interface{} { matchFunc := func(s string) bool {