From fd9c7310d1a4f29dfdeef465798cd332ca2827d6 Mon Sep 17 00:00:00 2001 From: haanhvu Date: Wed, 3 Jan 2024 01:41:36 +0700 Subject: [PATCH] Implement spans exporting in ClickHouse storage Signed-off-by: haanhvu --- .../exporters/storageexporter/exporter.go | 23 ++- .../exporters/storageexporter/factory.go | 3 +- .../extension/jaegerstorage/config.go | 6 +- .../extension/jaegerstorage/extension.go | 9 ++ config.yaml | 51 +++++++ go.mod | 10 +- go.sum | 32 ++++ idl | 2 +- plugin/storage/clickhouse/config.go | 84 +++++++++++ plugin/storage/clickhouse/factory.go | 72 +++++++++ plugin/storage/clickhouse/factory_test.go | 142 ++++++++++++++++++ .../clickhouse/spanstore/empty_test.go | 4 + .../storage/clickhouse/spanstore/exporter.go | 130 ++++++++++++++++ plugin/storage/clickhouse/spanstore/schema.go | 41 +++++ 14 files changed, 599 insertions(+), 10 deletions(-) create mode 100644 config.yaml create mode 100644 plugin/storage/clickhouse/config.go create mode 100644 plugin/storage/clickhouse/factory.go create mode 100644 plugin/storage/clickhouse/factory_test.go create mode 100644 plugin/storage/clickhouse/spanstore/empty_test.go create mode 100644 plugin/storage/clickhouse/spanstore/exporter.go create mode 100644 plugin/storage/clickhouse/spanstore/schema.go diff --git a/cmd/jaeger/internal/exporters/storageexporter/exporter.go b/cmd/jaeger/internal/exporters/storageexporter/exporter.go index 68aa330a8f4..271d573c55e 100644 --- a/cmd/jaeger/internal/exporters/storageexporter/exporter.go +++ b/cmd/jaeger/internal/exporters/storageexporter/exporter.go @@ -14,6 +14,7 @@ import ( "go.uber.org/zap" "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerstorage" + ch "github.com/jaegertracing/jaeger/plugin/storage/clickhouse" "github.com/jaegertracing/jaeger/storage/spanstore" ) @@ -21,6 +22,10 @@ type storageExporter struct { config *Config logger *zap.Logger spanWriter spanstore.Writer + clickhouse bool + // Separate traces exporting function for ClickHouse storage. + // This is temporary until we have v2 storage API. + chExportTraces func(ctx context.Context, td ptrace.Traces) error } func newExporter(config *Config, otel component.TelemetrySettings) *storageExporter { @@ -30,14 +35,22 @@ func newExporter(config *Config, otel component.TelemetrySettings) *storageExpor } } -func (exp *storageExporter) start(_ context.Context, host component.Host) error { +func (exp *storageExporter) start(ctx context.Context, host component.Host) error { f, err := jaegerstorage.GetStorageFactory(exp.config.TraceStorage, host) if err != nil { return fmt.Errorf("cannot find storage factory: %w", err) } - if exp.spanWriter, err = f.CreateSpanWriter(); err != nil { - return fmt.Errorf("cannot create span writer: %w", err) + switch t := f.(type) { + case *ch.Factory: + exp.clickhouse = true + t.CreateSpansTable(ctx) + exp.chExportTraces = t.ExportSpans + default: + exp.clickhouse = false + if exp.spanWriter, err = f.CreateSpanWriter(); err != nil { + return fmt.Errorf("cannot create span writer: %w", err) + } } return nil @@ -49,6 +62,10 @@ func (exp *storageExporter) close(_ context.Context) error { } func (exp *storageExporter) pushTraces(ctx context.Context, td ptrace.Traces) error { + if exp.clickhouse { + return exp.chExportTraces(ctx, td) + } + batches, err := otlp2jaeger.ProtoFromTraces(td) if err != nil { return fmt.Errorf("cannot transform OTLP traces to Jaeger format: %w", err) diff --git a/cmd/jaeger/internal/exporters/storageexporter/factory.go b/cmd/jaeger/internal/exporters/storageexporter/factory.go index 5721c45c4e4..95a66ce0c06 100644 --- a/cmd/jaeger/internal/exporters/storageexporter/factory.go +++ b/cmd/jaeger/internal/exporters/storageexporter/factory.go @@ -40,7 +40,8 @@ func createTracesExporter(ctx context.Context, set exporter.CreateSettings, conf // Disable Timeout/RetryOnFailure and SendingQueue exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}), exporterhelper.WithRetry(exporterhelper.RetrySettings{Enabled: false}), - exporterhelper.WithQueue(exporterhelper.QueueSettings{Enabled: false}), + // Enable queue settings for Clickhouse only + exporterhelper.WithQueue(exporterhelper.QueueSettings{Enabled: ex.clickhouse}), exporterhelper.WithStart(ex.start), exporterhelper.WithShutdown(ex.close), ) diff --git a/cmd/jaeger/internal/extension/jaegerstorage/config.go b/cmd/jaeger/internal/extension/jaegerstorage/config.go index 69f339926ec..269dae56b2a 100644 --- a/cmd/jaeger/internal/extension/jaegerstorage/config.go +++ b/cmd/jaeger/internal/extension/jaegerstorage/config.go @@ -5,6 +5,7 @@ package jaegerstorage import ( memoryCfg "github.com/jaegertracing/jaeger/pkg/memory/config" + ch "github.com/jaegertracing/jaeger/plugin/storage/clickhouse" ) // Config has the configuration for jaeger-query, @@ -13,9 +14,6 @@ type Config struct { // TODO add other storage types here // TODO how will this work with 3rd party storage implementations? // Option: instead of looking for specific name, check interface. -} -type MemoryStorage struct { - Name string `mapstructure:"name"` - memoryCfg.Configuration + ClickHouse map[string]ch.Config `mapstructure:"clickhouse"` } diff --git a/cmd/jaeger/internal/extension/jaegerstorage/extension.go b/cmd/jaeger/internal/extension/jaegerstorage/extension.go index 60db1d9ec9e..4dd4924a5df 100644 --- a/cmd/jaeger/internal/extension/jaegerstorage/extension.go +++ b/cmd/jaeger/internal/extension/jaegerstorage/extension.go @@ -14,6 +14,7 @@ import ( "go.uber.org/zap" "github.com/jaegertracing/jaeger/pkg/metrics" + "github.com/jaegertracing/jaeger/plugin/storage/clickhouse" "github.com/jaegertracing/jaeger/plugin/storage/memory" "github.com/jaegertracing/jaeger/storage" ) @@ -71,6 +72,14 @@ func (s *storageExt) Start(ctx context.Context, host component.Host) error { ) } // TODO add support for other backends + + for name, chCfg := range s.config.ClickHouse { + if _, ok := s.factories[name]; ok { + return fmt.Errorf("duplicate clickhouse storage name %s", name) + } + s.factories[name] = clickhouse.NewFactory(ctx, chCfg, s.logger.With(zap.String("storage_name", name))) + } + return nil } diff --git a/config.yaml b/config.yaml new file mode 100644 index 00000000000..36f40e90b0a --- /dev/null +++ b/config.yaml @@ -0,0 +1,51 @@ +service: + extensions: [jaeger_storage, jaeger_query] + pipelines: + traces: + receivers: [otlp, jaeger, zipkin] + processors: [batch] + exporters: [jaeger_storage_exporter] + +extensions: + # health_check: + # pprof: + # endpoint: 0.0.0.0:1777 + # zpages: + # endpoint: 0.0.0.0:55679 + + jaeger_query: + trace_storage: ch_store + ui_config: ./cmd/jaeger/config-ui.json + + jaeger_storage: + memory: + memstore: + max_traces: 100000 + memstore_archive: + max_traces: 100000 + clickhouse: + ch_store: + endpoint: tcp://127.0.0.1:9000?dial_timeout=10s&compress=lz4 + spans_table_name: jaeger_spans + +receivers: + otlp: + protocols: + grpc: + http: + + jaeger: + protocols: + grpc: + thrift_binary: + thrift_compact: + thrift_http: + + zipkin: + +processors: + batch: + +exporters: + jaeger_storage_exporter: + trace_storage: ch_store diff --git a/go.mod b/go.mod index 856bd3ef276..3c7985d1f73 100644 --- a/go.mod +++ b/go.mod @@ -83,8 +83,11 @@ require ( require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect + github.com/ClickHouse/ch-go v0.58.2 // indirect + github.com/ClickHouse/clickhouse-go/v2 v2.15.0 // indirect github.com/IBM/sarama v1.42.1 // indirect github.com/VividCortex/gohistogram v1.0.0 // indirect + github.com/andybalholm/brotli v1.0.6 // indirect github.com/aws/aws-sdk-go v1.48.14 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect @@ -93,13 +96,15 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/eapache/go-resiliency v1.4.0 // indirect github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect github.com/eapache/queue v1.1.0 // indirect github.com/elastic/elastic-transport-go/v8 v8.3.0 // indirect github.com/fatih/color v1.14.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-faster/city v1.0.1 // indirect + github.com/go-faster/errors v0.6.1 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.3.0 // indirect @@ -157,6 +162,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/zipkin v0.91.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/openzipkin/zipkin-go v0.4.2 // indirect + github.com/paulmach/orb v0.10.0 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pierrec/lz4/v4 v4.1.18 // indirect @@ -172,8 +178,10 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/segmentio/asm v1.2.0 // indirect github.com/shirou/gopsutil/v3 v3.23.11 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/shopspring/decimal v1.3.1 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect diff --git a/go.sum b/go.sum index 0016b6d1747..c82f79f6e54 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,12 @@ contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ClickHouse/ch-go v0.58.2 h1:jSm2szHbT9MCAB1rJ3WuCJqmGLi5UTjlNu+f530UTS0= +github.com/ClickHouse/ch-go v0.58.2/go.mod h1:Ap/0bEmiLa14gYjCiRkYGbXvbe8vwdrfTYWhsuQ99aw= +github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0= +github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= +github.com/ClickHouse/clickhouse-go/v2 v2.15.0 h1:G0hTKyO8fXXR1bGnZ0DY3vTG01xYfOGW76zgjg5tmC4= +github.com/ClickHouse/clickhouse-go/v2 v2.15.0/go.mod h1:kXt1SRq0PIRa6aKZD7TnFnY9PQKmc2b13sHtOYcK6cQ= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/IBM/sarama v1.42.1 h1:wugyWa15TDEHh2kvq2gAy1IHLjEjuYOYgXz/ruC/OSQ= @@ -57,6 +63,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= +github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/apache/thrift v0.19.0 h1:sOqkWPzMj7w6XaYbJQG7m4sGqVolaW/0D28Ln7yPzMk= github.com/apache/thrift v0.19.0/go.mod h1:SUALL216IiaOw2Oy+5Vs9lboJ/t9g40C+G07Dc0QC1I= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -113,6 +121,8 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-resiliency v1.4.0 h1:3OK9bWpPk5q6pbFAaYSEwD9CLUSHG8bnZuqX2yMt3B0= github.com/eapache/go-resiliency v1.4.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= @@ -145,6 +155,10 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= +github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= +github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06FI= +github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+Jpi2LAyY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -213,6 +227,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -227,6 +242,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -328,6 +344,7 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= @@ -387,6 +404,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mostynb/go-grpc-compression v1.2.2 h1:XaDbnRvt2+1vgr0b/l0qh4mJAfIxE0bKXtz2Znl3GGI= github.com/mostynb/go-grpc-compression v1.2.2/go.mod h1:GOCr2KBxXcblCuczg3YdLQlcin1/NfyDA348ckuCH6w= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -436,6 +454,9 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.4.2 h1:zjqfqHjUpPmB3c1GlCvvgsM1G4LkvqQbBDueDOCg/jA= github.com/openzipkin/zipkin-go v0.4.2/go.mod h1:ZeVkFjuuBiSy13y8vpSDCjMi9GoI3hPpCJSBx/EYFhY= +github.com/paulmach/orb v0.10.0 h1:guVYVqzxHE/CQ1KpfGO077TR0ATHSNjp4s6XGLn3W9s= +github.com/paulmach/orb v0.10.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= +github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= @@ -506,12 +527,16 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -560,6 +585,7 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tilinna/clock v1.1.0 h1:6IQQQCo6KoBxVudv6gwtY8o4eDfhHo8ojA5dP0MfhSs= github.com/tilinna/clock v1.1.0/go.mod h1:ZsP7BcY7sEEz7ktc0IVy8Us6boDrK8VradlKRUGfOao= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= @@ -581,6 +607,7 @@ github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgk github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -588,6 +615,7 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -727,6 +755,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= @@ -828,9 +857,11 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1053,6 +1084,7 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= diff --git a/idl b/idl index cd5d410a252..a23850bd182 160000 --- a/idl +++ b/idl @@ -1 +1 @@ -Subproject commit cd5d410a252cc7e4683ad2aa0e7ffe2263539e37 +Subproject commit a23850bd18284c0c5d6fa02aaa1cd69f4920ba9b diff --git a/plugin/storage/clickhouse/config.go b/plugin/storage/clickhouse/config.go new file mode 100644 index 00000000000..3ba74ac6945 --- /dev/null +++ b/plugin/storage/clickhouse/config.go @@ -0,0 +1,84 @@ +// Copyright (c) 2023 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package clickhouse + +import ( + "context" + "database/sql" + "errors" + "fmt" + "net/url" + + "github.com/ClickHouse/clickhouse-go/v2" +) + +type Config struct { + Endpoint string `mapstructure:"endpoint"` + Username string `mapstructure:"username"` + Password string `mapstructure:"password"` + Database string `mapstructure:"database"` + SpansTableName string `mapstructure:"spans_table_name"` + // Materialized views' names? +} + +var driverName = "clickhouse" + +const ( + defaultDatabase = "default" + defaultUsername = "default" + defaultPassword = "" +) + +func (cfg *Config) NewClient(ctx context.Context) (*sql.DB, error) { + if cfg.Endpoint == "" { + return nil, errors.New("no endpoints specified") + } + + dsnURL, err := url.Parse(cfg.Endpoint) + if err != nil { + return nil, err + } + + queryParams := dsnURL.Query() + + if dsnURL.Scheme == "https" { + queryParams.Set("secure", "true") + } + + if cfg.Database == "" { + cfg.Database = defaultDatabase + } + dsnURL.Path = cfg.Database + + if cfg.Username == "" { + cfg.Username = defaultUsername + } + + if cfg.Password == "" { + cfg.Password = defaultPassword + } + + dsnURL.User = url.UserPassword(cfg.Username, cfg.Password) + + dsnURL.RawQuery = queryParams.Encode() + + dsn := dsnURL.String() + + if _, err = clickhouse.ParseDSN(dsn); err != nil { + return nil, err + } + + db, err := sql.Open(driverName, dsn) + if err != nil { + return nil, err + } + + createDBquery := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", cfg.Database) + _, err = db.ExecContext(ctx, createDBquery) + if err != nil { + return nil, err + } + + return db, nil +} diff --git a/plugin/storage/clickhouse/factory.go b/plugin/storage/clickhouse/factory.go new file mode 100644 index 00000000000..8f773ff005f --- /dev/null +++ b/plugin/storage/clickhouse/factory.go @@ -0,0 +1,72 @@ +// Copyright (c) 2023 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package clickhouse + +import ( + "context" + "database/sql" + + "go.opentelemetry.io/collector/pdata/ptrace" + "go.uber.org/zap" + + "github.com/jaegertracing/jaeger/pkg/metrics" + chSpanStore "github.com/jaegertracing/jaeger/plugin/storage/clickhouse/spanstore" + "github.com/jaegertracing/jaeger/storage/dependencystore" + "github.com/jaegertracing/jaeger/storage/spanstore" +) + +type Factory struct { + client *sql.DB + spansTableName string + logger *zap.Logger +} + +func NewFactory(ctx context.Context, cfg Config, logger *zap.Logger) *Factory { + f := &Factory{ + logger: logger, + spansTableName: cfg.SpansTableName, + } + + client, err := cfg.NewClient(ctx) + if err != nil { + f.logger.Error("failed to create ClickHouse client", zap.Error(err)) + } else { + f.client = client + } + + return f + + // TODO: Move some steps to Initialize() +} + +func (f *Factory) CreateSpansTable(ctx context.Context) error { + if err := chSpanStore.CreateSpansTable(ctx, f.client, f.spansTableName); err != nil { + f.logger.Error("failed to create spans table", zap.Error(err)) + } + return nil +} + +func (f *Factory) ExportSpans(ctx context.Context, td ptrace.Traces) error { + if err := chSpanStore.ExportSpans(ctx, f.client, f.spansTableName, td); err != nil { + return err + } + + return nil +} + +func (f *Factory) Initialize(metricsFactory metrics.Factory, logger *zap.Logger) error { + return nil +} + +func (f *Factory) CreateSpanReader() (spanstore.Reader, error) { + return nil, nil +} + +func (f *Factory) CreateSpanWriter() (spanstore.Writer, error) { + return nil, nil +} + +func (f *Factory) CreateDependencyReader() (dependencystore.Reader, error) { + return nil, nil +} diff --git a/plugin/storage/clickhouse/factory_test.go b/plugin/storage/clickhouse/factory_test.go new file mode 100644 index 00000000000..da5147bf77d --- /dev/null +++ b/plugin/storage/clickhouse/factory_test.go @@ -0,0 +1,142 @@ +// Copyright (c) 2023 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package clickhouse + +import ( + "context" + "database/sql" + "database/sql/driver" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/ptrace" + "go.uber.org/zap" +) + +func TestExportSpans(t *testing.T) { + t.Run("create factory and export spans", func(t *testing.T) { + var items int + initClickhouseTestServer(t, func(query string, values []driver.Value) error { + if strings.HasPrefix(query, "INSERT") { + items++ + require.Equal(t, "test-operation", values[4]) + require.Equal(t, "test-service", values[5]) + require.Equal(t, []string{"attKey0", "attKey1"}, values[6]) + require.Equal(t, []string{"attVal0", "attVal1"}, values[7]) + } + return nil + }) + + c := Config{ + Endpoint: "clickhouse://127.0.0.1:9000", + SpansTableName: "jaeger_spans", + } + + f := NewFactory(context.TODO(), c, zap.NewNop()) + require.NotNil(t, f.client) + + err := f.CreateSpansTable(context.TODO()) + require.NoError(t, err) + + err = f.ExportSpans(context.TODO(), createTraces(5)) + require.NoError(t, err) + + err = f.ExportSpans(context.TODO(), createTraces(10)) + require.NoError(t, err) + + require.Equal(t, 15, items) + }) +} + +func createTraces(count int) ptrace.Traces { + traces := ptrace.NewTraces() + rs := traces.ResourceSpans().AppendEmpty() + rs.Resource().Attributes().PutStr("service.name", "test-service") + ss := rs.ScopeSpans().AppendEmpty() + for i := 0; i < count; i++ { + s := ss.Spans().AppendEmpty() + s.SetStartTimestamp(pcommon.NewTimestampFromTime(time.Now())) + s.SetEndTimestamp(pcommon.NewTimestampFromTime(time.Now())) + s.Attributes().PutStr("attKey0", "attVal0") + s.Attributes().PutStr("attKey1", "attVal1") + s.SetName("test-operation") + } + return traces +} + +func initClickhouseTestServer(t *testing.T, recorder recorder) { + driverName = "test" + sql.Register(driverName, &testClickhouseDriver{ + recorder: recorder, + }) +} + +type recorder func(query string, values []driver.Value) error + +type testClickhouseDriver struct { + recorder recorder +} + +func (t *testClickhouseDriver) Open(_ string) (driver.Conn, error) { + return &testClickhouseDriverConn{ + recorder: t.recorder, + }, nil +} + +type testClickhouseDriverConn struct { + recorder recorder +} + +func (*testClickhouseDriverConn) Begin() (driver.Tx, error) { + return &testClickhouseDriverTx{}, nil +} + +func (*testClickhouseDriverConn) Close() error { + return nil +} + +func (t *testClickhouseDriverConn) Prepare(query string) (driver.Stmt, error) { + return &testClickhouseDriverStmt{ + query: query, + recorder: t.recorder, + }, nil +} + +func (*testClickhouseDriverConn) CheckNamedValue(_ *driver.NamedValue) error { + return nil +} + +type testClickhouseDriverTx struct{} + +func (*testClickhouseDriverTx) Commit() error { + return nil +} + +func (*testClickhouseDriverTx) Rollback() error { + return nil +} + +type testClickhouseDriverStmt struct { + query string + recorder recorder +} + +func (*testClickhouseDriverStmt) Close() error { + return nil +} + +func (t *testClickhouseDriverStmt) Exec(args []driver.Value) (driver.Result, error) { + return nil, t.recorder(t.query, args) +} + +func (t *testClickhouseDriverStmt) NumInput() int { + return strings.Count(t.query, "?") +} + +func (t *testClickhouseDriverStmt) Query(_ []driver.Value) (driver.Rows, error) { + return nil, nil +} diff --git a/plugin/storage/clickhouse/spanstore/empty_test.go b/plugin/storage/clickhouse/spanstore/empty_test.go new file mode 100644 index 00000000000..9a0f6645c07 --- /dev/null +++ b/plugin/storage/clickhouse/spanstore/empty_test.go @@ -0,0 +1,4 @@ +// Copyright (c) 2023 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package spanstore diff --git a/plugin/storage/clickhouse/spanstore/exporter.go b/plugin/storage/clickhouse/spanstore/exporter.go new file mode 100644 index 00000000000..aa586708911 --- /dev/null +++ b/plugin/storage/clickhouse/spanstore/exporter.go @@ -0,0 +1,130 @@ +// Copyright (c) 2023 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package spanstore + +import ( + "context" + "database/sql" + "encoding/hex" + "fmt" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/ptrace" + conventions "go.opentelemetry.io/collector/semconv/v1.18.0" +) + +const ( + insertSpansSQL = `INSERT INTO %s ( + Timestamp, + TraceId, + SpanId, + ParentSpanId, + Operation, + Service, + Tags.keys, + Tags.values, + Duration + ) VALUES ( + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ) + ` +) + +func ExportSpans(ctx context.Context, db *sql.DB, tableName string, td ptrace.Traces) error { + tx, err := db.Begin() + if err != nil { + return err + } + + defer func() { + tx.Rollback() + }() + + if err = insertSpans(ctx, tx, tableName, td); err != nil { + return err + } + + return tx.Commit() +} + +func insertSpans(ctx context.Context, tx *sql.Tx, tableName string, td ptrace.Traces) error { + statement, err := tx.PrepareContext(ctx, fmt.Sprintf(insertSpansSQL, tableName)) + if err != nil { + return err + } + + defer func() { + _ = statement.Close() + }() + + for i := 0; i < td.ResourceSpans().Len(); i++ { + spans := td.ResourceSpans().At(i) + res := spans.Resource() + var serviceName string + if v, ok := res.Attributes().Get(conventions.AttributeServiceName); ok { + serviceName = v.Str() + } + for j := 0; j < spans.ScopeSpans().Len(); j++ { + rs := spans.ScopeSpans().At(j).Spans() + for k := 0; k < rs.Len(); k++ { + r := rs.At(k) + tagKeys, tagValues := attributesToArrays(r.Attributes()) + _, err = statement.ExecContext(ctx, + r.StartTimestamp().AsTime(), + traceIDToHexOrEmptyString(r.TraceID()), + spanIDToHexOrEmptyString(r.SpanID()), + spanIDToHexOrEmptyString(r.ParentSpanID()), + r.Name(), + serviceName, + tagKeys, + tagValues, + uint64(r.EndTimestamp().AsTime().Sub(r.StartTimestamp().AsTime()).Nanoseconds()), + ) + if err != nil { + return fmt.Errorf("exec context: %w", err) + } + } + } + } + + return nil +} + +func attributesToArrays(attributes pcommon.Map) ([]string, []string) { + keys := make([]string, 0) + values := make([]string, 0) + + attributes.Range(func(k string, v pcommon.Value) bool { + keys = append(keys, k) + values = append(values, v.AsString()) + return true + }) + return keys, values +} + +// spanIDToHexOrEmptyString returns a hex string from SpanID. +// An empty string is returned, if SpanID is empty. +func spanIDToHexOrEmptyString(id pcommon.SpanID) string { + if id.IsEmpty() { + return "" + } + return hex.EncodeToString(id[:]) +} + +// traceIDToHexOrEmptyString returns a hex string from TraceID. +// An empty string is returned, if TraceID is empty. +func traceIDToHexOrEmptyString(id pcommon.TraceID) string { + if id.IsEmpty() { + return "" + } + return hex.EncodeToString(id[:]) +} diff --git a/plugin/storage/clickhouse/spanstore/schema.go b/plugin/storage/clickhouse/spanstore/schema.go new file mode 100644 index 00000000000..de0cd2ccca3 --- /dev/null +++ b/plugin/storage/clickhouse/spanstore/schema.go @@ -0,0 +1,41 @@ +// Copyright (c) 2023 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package spanstore + +import ( + "context" + "database/sql" + "fmt" +) + +const ( + createSpansTableSQL = `CREATE TABLE IF NOT EXISTS %s ( + Timestamp DateTime64(9) CODEC(Delta, ZSTD(1)), + TraceId String CODEC(ZSTD(1)), + SpanId String CODEC(ZSTD(1)), + ParentSpanId String CODEC(ZSTD(1)), + Operation LowCardinality(String) CODEC(ZSTD(1)), + Service LowCardinality(String) CODEC(ZSTD(1)), + Tags Nested + ( + keys LowCardinality(String), + values String + ) CODEC (ZSTD(1)), + Duration UInt64 CODEC(ZSTD(1)), + INDEX idx_trace_id TraceId TYPE bloom_filter(0.001) GRANULARITY 1, + INDEX idx_tags_keys Tags.keys TYPE bloom_filter(0.01) GRANULARITY 1, + INDEX idx_tags_values Tags.values TYPE bloom_filter(0.01) GRANULARITY 1, + INDEX idx_duration Duration TYPE minmax GRANULARITY 1 + ) ENGINE MergeTree() + + PARTITION BY toDate(Timestamp) + ORDER BY (Service, Operation, toUnixTimestamp(Timestamp), Duration, TraceId) + SETTINGS index_granularity=8192, ttl_only_drop_parts = 1; + ` +) + +func CreateSpansTable(ctx context.Context, db *sql.DB, tableName string) error { + _, err := db.ExecContext(ctx, fmt.Sprintf(createSpansTableSQL, tableName)) + return err +}