Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions internal/storage/elasticsearch/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ 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"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add validation that if SpanIndexOverride is not empty then UseReadWriteAliases must be true, otherwise throw an error

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similarly, add validation that if any of the other fields like prefix / suffix are defined that an error is thrown saying they are incompatible with Override setting.

// IndexSpanAlias is an explicit alias name for span indices.
// When set, Jaeger will use this alias directly instead of the prefix+wildcard pattern.
// This allows integration with existing Elasticsearch setups and custom index management.
IndexSpanAlias string `mapstructure:"index_span_alias"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call this span_index_override

Suggested change
IndexSpanAlias string `mapstructure:"index_span_alias"`
// 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"`

// IndexServiceAlias is an explicit alias name for service indices.
// When set, Jaeger will use this alias directly instead of the prefix+wildcard pattern.
IndexServiceAlias string `mapstructure:"index_service_alias"`
// 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
4 changes: 4 additions & 0 deletions internal/storage/v1/elasticsearch/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ func (f *FactoryBase) GetSpanReaderParams() esSpanStore.SpanReaderParams {
UseReadWriteAliases: f.config.UseReadWriteAliases,
ReadAliasSuffix: f.config.ReadAliasSuffix,
RemoteReadClusters: f.config.RemoteReadClusters,
SpanAlias: f.config.IndexSpanAlias,
ServiceAlias: f.config.IndexServiceAlias,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the same field names as in the config struct

Logger: f.logger,
Tracer: f.tracer.Tracer("esSpanStore.SpanReader"),
}
Expand All @@ -137,6 +139,8 @@ func (f *FactoryBase) GetSpanWriterParams() esSpanStore.SpanWriterParams {
TagDotReplacement: f.config.Tags.DotReplacement,
UseReadWriteAliases: f.config.UseReadWriteAliases,
WriteAliasSuffix: f.config.WriteAliasSuffix,
SpanAlias: f.config.IndexSpanAlias,
ServiceAlias: f.config.IndexServiceAlias,
Logger: f.logger,
MetricsFactory: f.metricsFactory,
ServiceCacheTTL: f.config.ServiceCacheTTL,
Expand Down
73 changes: 57 additions & 16 deletions internal/storage/v1/elasticsearch/spanstore/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,37 +124,78 @@ type SpanReaderParams struct {
ReadAliasSuffix string
UseReadWriteAliases bool
RemoteReadClusters []string
Logger *zap.Logger
Tracer trace.Tracer
// SpanAlias is an explicit alias name for span indices.
// When set, this alias will be used instead of the IndexPrefix pattern.
SpanAlias string
// ServiceAlias is an explicit alias name for service indices.
// When set, this alias will be used instead of the IndexPrefix pattern.
ServiceAlias string
Logger *zap.Logger
Tracer trace.Tracer
}

// NewSpanReader returns a new SpanReader with a metrics.
func NewSpanReader(p SpanReaderParams) *SpanReader {
// Determine span index prefix - use explicit alias if provided
spanIndexPrefix := p.IndexPrefix.Apply(spanIndexBaseName)
hasSpanAlias := p.SpanAlias != ""
if hasSpanAlias {
spanIndexPrefix = p.SpanAlias
}

// Determine service index prefix - use explicit alias if provided
serviceIndexPrefix := p.IndexPrefix.Apply(serviceIndexBaseName)
hasServiceAlias := p.ServiceAlias != ""
if hasServiceAlias {
serviceIndexPrefix = p.ServiceAlias
}

// When using explicit aliases, treat them like read/write aliases
// (no time-based index selection)
useAliasMode := p.UseReadWriteAliases || hasSpanAlias || hasServiceAlias
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't introduce this implicit behavior, instead raise an error overrides are provided but UseReadWriteAliases is false.


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 {
// When read/write aliases or explicit aliases are enabled, only the alias is queried and therefore should not affect performance.
if useAliasMode {
maxSpanAge = dawnOfTimeSpanAge
}

// Create custom time range function that handles explicit aliases independently
var timeRangeFn TimeRangeIndexFn
if hasSpanAlias || hasServiceAlias {
// For explicit aliases, we need to check each index prefix individually
// to determine if it's an alias (and should be returned as-is) or a regular prefix (needs date-based logic)
baseTimeRangeFn := TimeRangeIndicesFn(p.UseReadWriteAliases, p.ReadAliasSuffix, p.RemoteReadClusters)
timeRangeFn = func(indexPrefix, indexDateLayout string, startTime, endTime time.Time, reduceDuration time.Duration) []string {
// Check if this indexPrefix matches one of our explicit aliases
if (hasSpanAlias && indexPrefix == spanIndexPrefix) || (hasServiceAlias && indexPrefix == serviceIndexPrefix) {
// This is an explicit alias, return it as-is
return []string{indexPrefix}
}
// Not an explicit alias, use standard time-based logic
return baseTimeRangeFn(indexPrefix, indexDateLayout, startTime, endTime, reduceDuration)
}
} else {
// Use standard time range function
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,
SpanAlias: "custom-span-alias",
},
maxSpanAge: time.Hour * 24 * 365 * 50,
},
{
name: "explicit service alias",
params: SpanReaderParams{
MaxSpanAge: time.Hour * 72,
ServiceAlias: "custom-service-alias",
},
maxSpanAge: time.Hour * 24 * 365 * 50,
},
{
name: "both explicit aliases",
params: SpanReaderParams{
MaxSpanAge: time.Hour * 72,
SpanAlias: "custom-span-alias",
ServiceAlias: "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,
SpanAlias: "custom-span-alias",
ServiceAlias: "custom-service-alias",
},
indices: []string{"custom-span-alias", "custom-service-alias"},
},
{
params: SpanReaderParams{
SpanIndex: spanIndexOpts,
ServiceIndex: serviceIndexOpts,
SpanAlias: "custom-span-alias",
},
indices: []string{"custom-span-alias", serviceIndexBaseName + serviceDataLayoutFormat},
},
{
params: SpanReaderParams{
SpanIndex: spanIndexOpts,
ServiceIndex: serviceIndexOpts,
ServiceAlias: "custom-service-alias",
},
indices: []string{spanIndexBaseName + spanDataLayoutFormat, "custom-service-alias"},
},
{
params: SpanReaderParams{
SpanIndex: spanIndexOpts,
ServiceIndex: serviceIndexOpts,
IndexPrefix: "foo:",
SpanAlias: "custom-span-alias",
ServiceAlias: "custom-service-alias",
},
indices: []string{"custom-span-alias", "custom-service-alias"},
},
{
params: SpanReaderParams{
SpanIndex: spanIndexOpts,
Expand Down
41 changes: 40 additions & 1 deletion internal/storage/v1/elasticsearch/spanstore/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ type SpanWriterParams struct {
TagDotReplacement string
UseReadWriteAliases bool
WriteAliasSuffix string
ServiceCacheTTL time.Duration
// SpanAlias is an explicit alias name for span indices.
// When set, this alias will be used instead of the IndexPrefix pattern.
SpanAlias string
// ServiceAlias is an explicit alias name for service indices.
// When set, this alias will be used instead of the IndexPrefix pattern.
ServiceAlias string
ServiceCacheTTL time.Duration
}

// NewSpanWriter creates a new SpanWriter for use
Expand Down Expand Up @@ -102,13 +108,46 @@ func NewSpanWriter(p SpanWriterParams) *SpanWriter {
type spanAndServiceIndexFn func(spanTime time.Time) (string, string)

func getSpanAndServiceIndexFn(p SpanWriterParams, writeAlias string) spanAndServiceIndexFn {
// Use explicit aliases if provided, otherwise use prefix pattern
spanIndexPrefix := p.IndexPrefix.Apply(spanIndexBaseName)
hasSpanAlias := p.SpanAlias != ""
if hasSpanAlias {
spanIndexPrefix = p.SpanAlias
}

serviceIndexPrefix := p.IndexPrefix.Apply(serviceIndexBaseName)
hasServiceAlias := p.ServiceAlias != ""
if hasServiceAlias {
serviceIndexPrefix = p.ServiceAlias
}

// Explicit aliases take priority over UseReadWriteAliases
// When explicit aliases are set, use them as-is without any suffix
if hasSpanAlias || hasServiceAlias {
return func(date time.Time) (string, string) {
spanIndex := spanIndexPrefix
serviceIndex := serviceIndexPrefix

// Only apply date suffix to indices without explicit aliases
if !hasSpanAlias {
spanIndex = indexWithDate(spanIndexPrefix, p.SpanIndex.DateLayout, date)
}
if !hasServiceAlias {
serviceIndex = indexWithDate(serviceIndexPrefix, p.ServiceIndex.DateLayout, date)
}

return spanIndex, serviceIndex
}
}

// UseReadWriteAliases: append write suffix to the prefix-based index names
if p.UseReadWriteAliases {
return func(_ time.Time) (string, string) {
return spanIndexPrefix + writeAlias, serviceIndexPrefix + writeAlias
}
}

// Default: use date-based index naming
return func(date time.Time) (string, string) {
return indexWithDate(spanIndexPrefix, p.SpanIndex.DateLayout, date), indexWithDate(serviceIndexPrefix, p.ServiceIndex.DateLayout, date)
}
Expand Down
51 changes: 51 additions & 0 deletions internal/storage/v1/elasticsearch/spanstore/writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,57 @@ func TestSpanWriterIndices(t *testing.T) {
},
indices: []string{"foo:" + config.IndexPrefixSeparator + spanIndexBaseName + "archive", "foo:" + config.IndexPrefixSeparator + serviceIndexBaseName + "archive"},
},
{
params: SpanWriterParams{
Client: clientFn, Logger: logger, MetricsFactory: metricsFactory,
SpanIndex: spanIndexOpts, ServiceIndex: serviceIndexOpts,
SpanAlias: "custom-span-alias", ServiceAlias: "custom-service-alias",
},
indices: []string{"custom-span-alias", "custom-service-alias"},
},
{
params: SpanWriterParams{
Client: clientFn, Logger: logger, MetricsFactory: metricsFactory,
SpanIndex: spanIndexOpts, ServiceIndex: serviceIndexOpts,
SpanAlias: "custom-span-alias",
},
indices: []string{"custom-span-alias", serviceIndexBaseName + serviceDataLayoutFormat},
},
{
params: SpanWriterParams{
Client: clientFn, Logger: logger, MetricsFactory: metricsFactory,
SpanIndex: spanIndexOpts, ServiceIndex: serviceIndexOpts,
ServiceAlias: "custom-service-alias",
},
indices: []string{spanIndexBaseName + spanDataLayoutFormat, "custom-service-alias"},
},
{
params: SpanWriterParams{
Client: clientFn, Logger: logger, MetricsFactory: metricsFactory,
SpanIndex: spanIndexOpts, ServiceIndex: serviceIndexOpts, IndexPrefix: "foo:",
SpanAlias: "custom-span-alias", ServiceAlias: "custom-service-alias",
},
indices: []string{"custom-span-alias", "custom-service-alias"},
},
{
params: SpanWriterParams{
Client: clientFn, Logger: logger, MetricsFactory: metricsFactory,
SpanIndex: spanIndexOpts, ServiceIndex: serviceIndexOpts,
UseReadWriteAliases: true, WriteAliasSuffix: "write",
SpanAlias: "custom-span-alias",
},
indices: []string{"custom-span-alias", serviceIndexBaseName + serviceDataLayoutFormat},
},
{
params: SpanWriterParams{
Client: clientFn, Logger: logger, MetricsFactory: metricsFactory,
SpanIndex: spanIndexOpts, ServiceIndex: serviceIndexOpts,
UseReadWriteAliases: true, WriteAliasSuffix: "write",
SpanAlias: "custom-span-alias",
ServiceAlias: "custom-service-alias",
},
indices: []string{"custom-span-alias", "custom-service-alias"},
},
}
for _, testCase := range testCases {
w := NewSpanWriter(testCase.params)
Expand Down
Loading