Skip to content
36 changes: 36 additions & 0 deletions internal/storage/elasticsearch/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@ type Configuration struct {
// Use this option with Elasticsearch rollover API. It requires an external component
// to create aliases before startup and then performing its management.
UseReadWriteAliases bool `mapstructure:"use_aliases"`
// SpanIndexOverride allows full control over the name of the index alias.
// Overrides any index prefix / suffix settings.
// Can only be used with UseReadWriteAliases=true.
SpanIndexOverride string `mapstructure:"span_index_override"`
// ServiceIndexOverride allows full control over the name of the index alias.
// Overrides any index prefix / suffix settings.
// Can only be used with UseReadWriteAliases=true.
ServiceIndexOverride string `mapstructure:"service_index_override"`
// ReadAliasSuffix is the suffix to append to the index name used for reading.
// This configuration only exists to provide backwards compatibility for jaeger-v1
// which is why it is not exposed as a configuration option for jaeger-v2
Expand Down Expand Up @@ -701,5 +709,33 @@ func (c *Configuration) Validate() error {
if c.CreateIndexTemplates && c.UseILM {
return errors.New("when UseILM is set true, CreateIndexTemplates must be set to false and index templates must be created by init process of es-rollover app")
}

// Validate explicit override settings
hasSpanOverride := c.SpanIndexOverride != ""
hasServiceOverride := c.ServiceIndexOverride != ""

// When explicit overrides are set, UseReadWriteAliases must be true
if (hasSpanOverride || hasServiceOverride) && !c.UseReadWriteAliases {
return errors.New("when SpanIndexOverride or ServiceIndexOverride is set, UseReadWriteAliases must be true")
}

// When BOTH explicit overrides are set, prefix/suffix configurations are incompatible
// (because IndexPrefix would not be used at all in this case)
if hasSpanOverride && hasServiceOverride {
hasPrefix := c.Indices.IndexPrefix != ""
hasReadSuffix := c.ReadAliasSuffix != ""
hasWriteSuffix := c.WriteAliasSuffix != ""

if hasPrefix {
return errors.New("IndexPrefix is incompatible with override settings when both SpanIndexOverride and ServiceIndexOverride are set")
}
if hasReadSuffix {
return errors.New("ReadAliasSuffix is incompatible with override settings when both SpanIndexOverride and ServiceIndexOverride are set")
}
if hasWriteSuffix {
return errors.New("WriteAliasSuffix is incompatible with override settings when both SpanIndexOverride and ServiceIndexOverride are set")
}
}

return nil
}
71 changes: 71 additions & 0 deletions internal/storage/elasticsearch/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,77 @@ func TestValidate(t *testing.T) {
config: &Configuration{Servers: []string{"localhost:8000/dummyserver"}, UseILM: true, CreateIndexTemplates: true, UseReadWriteAliases: true},
expectedError: "when UseILM is set true, CreateIndexTemplates must be set to false and index templates must be created by init process of es-rollover app",
},
{
name: "explicit span override without UseReadWriteAliases",
config: &Configuration{
Servers: []string{"localhost:8000/dummyserver"},
SpanIndexOverride: "custom-span-override",
},
expectedError: "when SpanIndexOverride or ServiceIndexOverride is set, UseReadWriteAliases must be true",
},
{
name: "explicit service override without UseReadWriteAliases",
config: &Configuration{
Servers: []string{"localhost:8000/dummyserver"},
ServiceIndexOverride: "custom-service-override",
},
expectedError: "when SpanIndexOverride or ServiceIndexOverride is set, UseReadWriteAliases must be true",
},
{
name: "explicit overrides with UseReadWriteAliases is valid",
config: &Configuration{
Servers: []string{"localhost:8000/dummyserver"},
UseReadWriteAliases: true,
SpanIndexOverride: "custom-span-override",
ServiceIndexOverride: "custom-service-override",
},
},
{
name: "single explicit override with IndexPrefix is valid",
config: &Configuration{
Servers: []string{"localhost:8000/dummyserver"},
UseReadWriteAliases: true,
SpanIndexOverride: "custom-span-override",
Indices: Indices{
IndexPrefix: "prod",
},
},
},
{
name: "both explicit overrides with IndexPrefix is incompatible",
config: &Configuration{
Servers: []string{"localhost:8000/dummyserver"},
UseReadWriteAliases: true,
SpanIndexOverride: "custom-span-override",
ServiceIndexOverride: "custom-service-override",
Indices: Indices{
IndexPrefix: "prod",
},
},
expectedError: "IndexPrefix is incompatible with override settings when both",
},
{
name: "both explicit overrides with ReadAliasSuffix is incompatible",
config: &Configuration{
Servers: []string{"localhost:8000/dummyserver"},
UseReadWriteAliases: true,
SpanIndexOverride: "custom-span-override",
ServiceIndexOverride: "custom-service-override",
ReadAliasSuffix: "read",
},
expectedError: "ReadAliasSuffix is incompatible with override settings when both",
},
{
name: "both explicit overrides with WriteAliasSuffix is incompatible",
config: &Configuration{
Servers: []string{"localhost:8000/dummyserver"},
UseReadWriteAliases: true,
SpanIndexOverride: "custom-span-override",
ServiceIndexOverride: "custom-service-override",
WriteAliasSuffix: "write",
},
expectedError: "WriteAliasSuffix is incompatible with override settings when both",
},
}

for _, test := range tests {
Expand Down
52 changes: 28 additions & 24 deletions internal/storage/v1/elasticsearch/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,36 +110,40 @@ func (f *FactoryBase) getClient() es.Client {
// GetSpanReaderParams returns the SpanReaderParams which can be used to initialize the v1 and v2 readers.
func (f *FactoryBase) GetSpanReaderParams() esspanstore.SpanReaderParams {
return esspanstore.SpanReaderParams{
Client: f.getClient,
MaxDocCount: f.config.MaxDocCount,
MaxSpanAge: f.config.MaxSpanAge,
IndexPrefix: f.config.Indices.IndexPrefix,
SpanIndex: f.config.Indices.Spans,
ServiceIndex: f.config.Indices.Services,
TagDotReplacement: f.config.Tags.DotReplacement,
UseReadWriteAliases: f.config.UseReadWriteAliases,
ReadAliasSuffix: f.config.ReadAliasSuffix,
RemoteReadClusters: f.config.RemoteReadClusters,
Logger: f.logger,
Tracer: f.tracer.Tracer("esspanstore.SpanReader"),
Client: f.getClient,
MaxDocCount: f.config.MaxDocCount,
MaxSpanAge: f.config.MaxSpanAge,
IndexPrefix: f.config.Indices.IndexPrefix,
SpanIndex: f.config.Indices.Spans,
ServiceIndex: f.config.Indices.Services,
TagDotReplacement: f.config.Tags.DotReplacement,
UseReadWriteAliases: f.config.UseReadWriteAliases,
ReadAliasSuffix: f.config.ReadAliasSuffix,
RemoteReadClusters: f.config.RemoteReadClusters,
SpanIndexOverride: f.config.SpanIndexOverride,
ServiceIndexOverride: f.config.ServiceIndexOverride,
Logger: f.logger,
Tracer: f.tracer.Tracer("esspanstore.SpanReader"),
}
}

// GetSpanWriterParams returns the SpanWriterParams which can be used to initialize the v1 and v2 writers.
func (f *FactoryBase) GetSpanWriterParams() esspanstore.SpanWriterParams {
return esspanstore.SpanWriterParams{
Client: f.getClient,
IndexPrefix: f.config.Indices.IndexPrefix,
SpanIndex: f.config.Indices.Spans,
ServiceIndex: f.config.Indices.Services,
AllTagsAsFields: f.config.Tags.AllAsFields,
TagKeysAsFields: f.tags,
TagDotReplacement: f.config.Tags.DotReplacement,
UseReadWriteAliases: f.config.UseReadWriteAliases,
WriteAliasSuffix: f.config.WriteAliasSuffix,
Logger: f.logger,
MetricsFactory: f.metricsFactory,
ServiceCacheTTL: f.config.ServiceCacheTTL,
Client: f.getClient,
IndexPrefix: f.config.Indices.IndexPrefix,
SpanIndex: f.config.Indices.Spans,
ServiceIndex: f.config.Indices.Services,
AllTagsAsFields: f.config.Tags.AllAsFields,
TagKeysAsFields: f.tags,
TagDotReplacement: f.config.Tags.DotReplacement,
UseReadWriteAliases: f.config.UseReadWriteAliases,
WriteAliasSuffix: f.config.WriteAliasSuffix,
SpanIndexOverride: f.config.SpanIndexOverride,
ServiceIndexOverride: f.config.ServiceIndexOverride,
Logger: f.logger,
MetricsFactory: f.metricsFactory,
ServiceCacheTTL: f.config.ServiceCacheTTL,
}
}

Expand Down
78 changes: 51 additions & 27 deletions internal/storage/v1/elasticsearch/spanstore/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,47 +114,71 @@ type SpanReader struct {

// SpanReaderParams holds constructor params for NewSpanReader
type SpanReaderParams struct {
Client func() es.Client
MaxSpanAge time.Duration
MaxDocCount int
IndexPrefix cfg.IndexPrefix
SpanIndex cfg.IndexOptions
ServiceIndex cfg.IndexOptions
TagDotReplacement string
ReadAliasSuffix string
UseReadWriteAliases bool
RemoteReadClusters []string
Logger *zap.Logger
Tracer trace.Tracer
Client func() es.Client
MaxSpanAge time.Duration
MaxDocCount int
IndexPrefix cfg.IndexPrefix
SpanIndex cfg.IndexOptions
ServiceIndex cfg.IndexOptions
TagDotReplacement string
ReadAliasSuffix string
UseReadWriteAliases bool
RemoteReadClusters []string
SpanIndexOverride string
ServiceIndexOverride string
Logger *zap.Logger
Tracer trace.Tracer
}

// NewSpanReader returns a new SpanReader with a metrics.
func NewSpanReader(p SpanReaderParams) *SpanReader {
spanIndexPrefix := p.IndexPrefix.Apply(spanIndexBaseName)
hasSpanOverride := p.SpanIndexOverride != ""
if hasSpanOverride {
spanIndexPrefix = p.SpanIndexOverride
}

serviceIndexPrefix := p.IndexPrefix.Apply(serviceIndexBaseName)
hasServiceOverride := p.ServiceIndexOverride != ""
if hasServiceOverride {
serviceIndexPrefix = p.ServiceIndexOverride
}

useAliasMode := p.UseReadWriteAliases || hasSpanOverride || hasServiceOverride

maxSpanAge := p.MaxSpanAge
// Setting the maxSpanAge to a large duration will ensure all spans in the "read" alias are accessible by queries (query window = [now - maxSpanAge, now]).
// When read/write aliases are enabled, which are required for index rollovers, only the "read" alias is queried and therefore should not affect performance.
if p.UseReadWriteAliases {
if useAliasMode {
maxSpanAge = dawnOfTimeSpanAge
}

var timeRangeFn TimeRangeIndexFn
if hasSpanOverride || hasServiceOverride {
baseTimeRangeFn := TimeRangeIndicesFn(p.UseReadWriteAliases, p.ReadAliasSuffix, p.RemoteReadClusters)
timeRangeFn = func(indexPrefix, indexDateLayout string, startTime, endTime time.Time, reduceDuration time.Duration) []string {
if (hasSpanOverride && indexPrefix == spanIndexPrefix) || (hasServiceOverride && indexPrefix == serviceIndexPrefix) {
return []string{indexPrefix}
}
return baseTimeRangeFn(indexPrefix, indexDateLayout, startTime, endTime, reduceDuration)
}
} else {
timeRangeFn = TimeRangeIndicesFn(p.UseReadWriteAliases, p.ReadAliasSuffix, p.RemoteReadClusters)
}

return &SpanReader{
client: p.Client,
maxSpanAge: maxSpanAge,
serviceOperationStorage: NewServiceOperationStorage(p.Client, p.Logger, 0), // the decorator takes care of metrics
spanIndexPrefix: p.IndexPrefix.Apply(spanIndexBaseName),
serviceIndexPrefix: p.IndexPrefix.Apply(serviceIndexBaseName),
spanIndexPrefix: spanIndexPrefix,
serviceIndexPrefix: serviceIndexPrefix,
spanIndex: p.SpanIndex,
serviceIndex: p.ServiceIndex,
timeRangeIndices: LoggingTimeRangeIndexFn(
p.Logger,
TimeRangeIndicesFn(p.UseReadWriteAliases, p.ReadAliasSuffix, p.RemoteReadClusters),
),
sourceFn: getSourceFn(p.MaxDocCount),
maxDocCount: p.MaxDocCount,
useReadWriteAliases: p.UseReadWriteAliases,
logger: p.Logger,
tracer: p.Tracer,
dotReplacer: dbmodel.NewDotReplacer(p.TagDotReplacement),
timeRangeIndices: LoggingTimeRangeIndexFn(p.Logger, timeRangeFn),
sourceFn: getSourceFn(p.MaxDocCount),
maxDocCount: p.MaxDocCount,
useReadWriteAliases: useAliasMode,
logger: p.Logger,
tracer: p.Tracer,
dotReplacer: dbmodel.NewDotReplacer(p.TagDotReplacement),
}
}

Expand Down
60 changes: 60 additions & 0 deletions internal/storage/v1/elasticsearch/spanstore/reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,31 @@ func TestNewSpanReader(t *testing.T) {
},
maxSpanAge: time.Hour * 24 * 365 * 50,
},
{
name: "explicit span alias",
params: SpanReaderParams{
MaxSpanAge: time.Hour * 72,
SpanIndexOverride: "custom-span-alias",
},
maxSpanAge: time.Hour * 24 * 365 * 50,
},
{
name: "explicit service alias",
params: SpanReaderParams{
MaxSpanAge: time.Hour * 72,
ServiceIndexOverride: "custom-service-alias",
},
maxSpanAge: time.Hour * 24 * 365 * 50,
},
{
name: "both explicit aliases",
params: SpanReaderParams{
MaxSpanAge: time.Hour * 72,
SpanIndexOverride: "custom-span-alias",
ServiceIndexOverride: "custom-service-alias",
},
maxSpanAge: time.Hour * 24 * 365 * 50,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
Expand Down Expand Up @@ -244,6 +269,41 @@ func TestSpanReaderIndices(t *testing.T) {
},
indices: []string{"foo:" + config.IndexPrefixSeparator + spanIndexBaseName + "archive", "foo:" + config.IndexPrefixSeparator + serviceIndexBaseName + "archive"},
},
{
params: SpanReaderParams{
SpanIndex: spanIndexOpts,
ServiceIndex: serviceIndexOpts,
SpanIndexOverride: "custom-span-alias",
ServiceIndexOverride: "custom-service-alias",
},
indices: []string{"custom-span-alias", "custom-service-alias"},
},
{
params: SpanReaderParams{
SpanIndex: spanIndexOpts,
ServiceIndex: serviceIndexOpts,
SpanIndexOverride: "custom-span-alias",
},
indices: []string{"custom-span-alias", serviceIndexBaseName + serviceDataLayoutFormat},
},
{
params: SpanReaderParams{
SpanIndex: spanIndexOpts,
ServiceIndex: serviceIndexOpts,
ServiceIndexOverride: "custom-service-alias",
},
indices: []string{spanIndexBaseName + spanDataLayoutFormat, "custom-service-alias"},
},
{
params: SpanReaderParams{
SpanIndex: spanIndexOpts,
ServiceIndex: serviceIndexOpts,
IndexPrefix: "foo:",
SpanIndexOverride: "custom-span-alias",
ServiceIndexOverride: "custom-service-alias",
},
indices: []string{"custom-span-alias", "custom-service-alias"},
},
{
params: SpanReaderParams{
SpanIndex: spanIndexOpts,
Expand Down
Loading
Loading