Skip to content

Commit 1a50fff

Browse files
authored
Replace non-ASCII µs suffix in telemetry metric names (#4776)
## Summary - Renames the microsecond-unit suffix used by several telemetry metric and span-attribute names from the non-ASCII `µs`/`μs` characters to plain-ASCII `us`, since backends such as Mimir/Prometheus reject metric names containing non-ASCII characters and were dropping these metrics on ingestion. - Covers the shape DB connection pool checkout queue-time metric named directly in the issue, plus several interval/span-duration attribute names elsewhere in the sync-service that used the same non-ASCII suffix, for consistency. - Adds coverage confirming the renamed metric is exported with a plain-ASCII name. Fixes #4732
1 parent 5ce464c commit 1a50fff

9 files changed

Lines changed: 40 additions & 33 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@core/sync-service": minor
3+
"@core/electric-telemetry": minor
4+
---
5+
6+
**Breaking change**: renamed several telemetry metric and span attribute names, replacing their non-ASCII `µs`/`μs` microsecond suffix with the plain-ASCII `us`. Some metrics backends (e.g. Mimir/Prometheus) reject metric names containing non-ASCII characters, which previously caused ingestion of the affected metrics to fail. Any dashboards, alerts, or queries referencing the old attribute names (e.g. `shape_db.pool.checkout.queue_time_μs`) need to be updated to the new ASCII names (e.g. `shape_db.pool.checkout.queue_time_us`).

‎integration-tests/tests/otel-export.lux‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
[invoke grep_otel_collector_output "Metric electric\.storage\.snapshot_stored\.operations .*stack_id=single_stack"]
121121
[invoke grep_otel_collector_output "Metric electric\.storage\.transaction_stored\.bytes .*stack_id=single_stack"]
122122
[invoke grep_otel_collector_output "Metric electric\.storage\.transaction_stored\.operations .*stack_id=single_stack"]
123+
[invoke grep_otel_collector_output "Metric electric\.shape_db\.pool\.checkout\.queue_time_us .*stack_id=single_stack"]
123124

124125
# The initial snapshot query span is exported from the Snapshotter path."]
125126
[invoke grep_otel_collector_output "Span shape_snapshot\.execute_for_shape .*shape\.query_reason=initial_snapshot"]

‎packages/electric-telemetry/lib/electric/telemetry/stack_telemetry.ex‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ defmodule ElectricTelemetry.StackTelemetry do
120120
sum("electric.subqueries.subset_result.count"),
121121
sum("electric.subqueries.move_in_triggered.count"),
122122
last_value("electric.postgres.info_looked_up.pg_version"),
123-
distribution("electric.shape_db.pool.checkout.queue_time_μs", unit: :microsecond),
123+
distribution("electric.shape_db.pool.checkout.queue_time_us", unit: :microsecond),
124124
last_value("electric.connection.consumers_ready.duration",
125125
unit: {:native, :millisecond}
126126
),

‎packages/sync-service/lib/electric/postgres/snapshot_query.ex‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ defmodule Electric.Postgres.SnapshotQuery do
5151
span_attrs,
5252
stack_id,
5353
fn ->
54-
OpenTelemetry.start_interval(:"shape_snapshot.checkout_wait.duration_µs")
54+
OpenTelemetry.start_interval(:"shape_snapshot.checkout_wait.duration_us")
5555

5656
Postgrex.transaction(
5757
pool,
5858
fn conn ->
59-
OpenTelemetry.start_interval(:"shape_snapshot.setup.duration_µs")
59+
OpenTelemetry.start_interval(:"shape_snapshot.setup.duration_us")
6060

6161
ctx = %{
6262
conn: conn,
@@ -79,7 +79,7 @@ defmodule Electric.Postgres.SnapshotQuery do
7979
span_name: "shape_snapshot.set_display_settings"
8080
)
8181

82-
OpenTelemetry.start_interval(:"shape_snapshot.query.duration_µs")
82+
OpenTelemetry.start_interval(:"shape_snapshot.query.duration_us")
8383

8484
result =
8585
OpenTelemetry.with_child_span(
@@ -90,7 +90,7 @@ defmodule Electric.Postgres.SnapshotQuery do
9090
)
9191

9292
OpenTelemetry.stop_and_save_intervals(
93-
total_attribute: :"shape_snapshot.total.duration_µs"
93+
total_attribute: :"shape_snapshot.total.duration_us"
9494
)
9595

9696
result

‎packages/sync-service/lib/electric/replication/shape_log_collector.ex‎

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,7 @@ defmodule Electric.Replication.ShapeLogCollector do
750750
],
751751
state.stack_id,
752752
fn ->
753-
OpenTelemetry.start_interval(:"shape_log_collector.logging.duration_µs")
753+
OpenTelemetry.start_interval(:"shape_log_collector.logging.duration_us")
754754

755755
Logger.debug(
756756
fn ->
@@ -768,7 +768,7 @@ defmodule Electric.Replication.ShapeLogCollector do
768768
result = handle_txn_fragment(state, txn_fragment)
769769

770770
OpenTelemetry.stop_and_save_intervals(
771-
total_attribute: :"shape_log_collector.transaction.total_duration_µs"
771+
total_attribute: :"shape_log_collector.transaction.total_duration_us"
772772
)
773773

774774
put_wall_clock_duration_if_commit(txn_fragment)
@@ -832,11 +832,11 @@ defmodule Electric.Replication.ShapeLogCollector do
832832
defp handle_txn_fragment(state, txn_fragment) do
833833
OpenTelemetry.add_span_attributes("txn.is_dropped": false)
834834

835-
OpenTelemetry.start_interval(:"shape_log_collector.fill_keys_in_txn.duration_µs")
835+
OpenTelemetry.start_interval(:"shape_log_collector.fill_keys_in_txn.duration_us")
836836

837837
case fill_keys(txn_fragment, state) do
838838
{:ok, txn_fragment} ->
839-
OpenTelemetry.start_interval(:"partitions.handle_transaction.duration_µs")
839+
OpenTelemetry.start_interval(:"partitions.handle_transaction.duration_us")
840840

841841
{partitions, txn_fragment} =
842842
Partitions.handle_txn_fragment(state.partitions, txn_fragment)
@@ -859,7 +859,7 @@ defmodule Electric.Replication.ShapeLogCollector do
859859
end
860860

861861
defp publish(state, event) do
862-
OpenTelemetry.start_interval(:"shape_log_collector.event_routing.duration_µs")
862+
OpenTelemetry.start_interval(:"shape_log_collector.event_routing.duration_us")
863863

864864
{events_by_handle, event_router} =
865865
EventRouter.event_by_shape_handle(state.event_router, event)
@@ -879,7 +879,7 @@ defmodule Electric.Replication.ShapeLogCollector do
879879
%{stack_id: state.stack_id}
880880
)
881881

882-
OpenTelemetry.start_interval(:"shape_log_collector.publish.duration_µs")
882+
OpenTelemetry.start_interval(:"shape_log_collector.publish.duration_us")
883883
context = OpenTelemetry.get_current_context()
884884

885885
{undeliverable, delivered_pids} =
@@ -920,7 +920,7 @@ defmodule Electric.Replication.ShapeLogCollector do
920920

921921
case event do
922922
%TransactionFragment{commit: commit} when not is_nil(commit) ->
923-
OpenTelemetry.start_interval(:"shape_log_collector.set_last_processed_lsn.duration_µs")
923+
OpenTelemetry.start_interval(:"shape_log_collector.set_last_processed_lsn.duration_us")
924924

925925
lsn = Lsn.from_integer(state.last_processed_offset.tx_offset)
926926
LsnTracker.set_last_processed_lsn(state.stack_id, lsn)
@@ -1033,22 +1033,22 @@ defmodule Electric.Replication.ShapeLogCollector do
10331033
if EventRouter.has_shape?(state.event_router, shape_handle) do
10341034
Logger.debug("Deleting shape #{shape_handle}")
10351035

1036-
OpenTelemetry.start_interval(:"unsubscribe_shape.remove_subscription.duration_µs")
1036+
OpenTelemetry.start_interval(:"unsubscribe_shape.remove_subscription.duration_us")
10371037

1038-
OpenTelemetry.start_interval(:"unsubscribe_shape.remove_from_event_router.duration_µs")
1038+
OpenTelemetry.start_interval(:"unsubscribe_shape.remove_from_event_router.duration_us")
10391039
event_router = EventRouter.remove_shape(state.event_router, shape_handle)
10401040

1041-
OpenTelemetry.start_interval(:"unsubscribe_shape.remove_from_partitions.duration_µs")
1041+
OpenTelemetry.start_interval(:"unsubscribe_shape.remove_from_partitions.duration_us")
10421042
partitions = Partitions.remove_shape(state.partitions, shape_handle)
10431043

1044-
OpenTelemetry.start_interval(:"unsubscribe_shape.demonitor_writer.duration_µs")
1044+
OpenTelemetry.start_interval(:"unsubscribe_shape.demonitor_writer.duration_us")
10451045
state = demonitor_writer(state, shape_handle)
10461046

1047-
OpenTelemetry.start_interval(:"unsubscribe_shape.remove_from_flush_tracker.duration_µs")
1047+
OpenTelemetry.start_interval(:"unsubscribe_shape.remove_from_flush_tracker.duration_us")
10481048
flush_tracker = FlushTracker.handle_shape_removed(state.flush_tracker, shape_handle)
10491049

10501050
OpenTelemetry.start_interval(
1051-
:"unsubscribe_shape.remove_from_dependency_layers.duration_µs"
1051+
:"unsubscribe_shape.remove_from_dependency_layers.duration_us"
10521052
)
10531053

10541054
dependency_layers =
@@ -1057,7 +1057,7 @@ defmodule Electric.Replication.ShapeLogCollector do
10571057
Electric.Shapes.ConsumerRegistry.remove_consumer(shape_handle, state.registry_state)
10581058

10591059
OpenTelemetry.stop_and_save_intervals(
1060-
total_attribute: "unsubscribe_shape.total_duration_µs"
1060+
total_attribute: "unsubscribe_shape.total_duration_us"
10611061
)
10621062

10631063
{:ok,

‎packages/sync-service/lib/electric/shape_cache/shape_cleaner.ex‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ defmodule Electric.ShapeCache.ShapeCleaner do
3131
fn ->
3232
valid_handles = remove_shapes_immediate(stack_id, shape_handles, reason)
3333
remove_shapes_deferred(stack_id, valid_handles)
34-
OpenTelemetry.stop_and_save_intervals(total_attribute: "remove_shape.total_duration_µs")
34+
OpenTelemetry.stop_and_save_intervals(total_attribute: "remove_shape.total_duration_us")
3535
:ok
3636
end
3737
)
@@ -155,18 +155,18 @@ defmodule Electric.ShapeCache.ShapeCleaner do
155155
end
156156

157157
defp remove_shape_immediate(stack_id, shape_handle, reason) do
158-
OpenTelemetry.start_interval(:"remove_shape.shape_status_remove.duration_µs")
158+
OpenTelemetry.start_interval(:"remove_shape.shape_status_remove.duration_us")
159159

160160
case Electric.ShapeCache.ShapeStatus.remove_shape(stack_id, shape_handle) do
161161
:ok ->
162-
OpenTelemetry.start_interval(:"remove_shape.shape_consumer_stop.duration_µs")
162+
OpenTelemetry.start_interval(:"remove_shape.shape_consumer_stop.duration_us")
163163

164164
stack_storage = Storage.for_stack(stack_id)
165165

166166
with :ok <- Consumer.stop(stack_id, shape_handle, reason),
167-
OpenTelemetry.start_interval(:"remove_shape.storage_cleanup.duration_µs"),
167+
OpenTelemetry.start_interval(:"remove_shape.storage_cleanup.duration_us"),
168168
:ok <- Storage.cleanup!(stack_storage, shape_handle),
169-
OpenTelemetry.start_interval(:"remove_shape.shape_log_collector_remove.duration_µs"),
169+
OpenTelemetry.start_interval(:"remove_shape.shape_log_collector_remove.duration_us"),
170170
:ok <-
171171
Electric.Replication.ShapeLogCollector.remove_shape(stack_id, shape_handle) do
172172
:ok
@@ -183,7 +183,7 @@ defmodule Electric.ShapeCache.ShapeCleaner do
183183
end
184184

185185
defp remove_shapes_deferred(stack_id, shape_handles) when is_list(shape_handles) do
186-
OpenTelemetry.start_interval(:"remove_shape.remove_shapes_deferred.duration_µs")
186+
OpenTelemetry.start_interval(:"remove_shape.remove_shapes_deferred.duration_us")
187187
:ok = CleanupTaskSupervisor.cleanup_async(stack_id, shape_handles)
188188
end
189189

‎packages/sync-service/lib/electric/shape_cache/shape_status/shape_db/connection.ex‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ defmodule Electric.ShapeCache.ShapeStatus.ShapeDb.Connection do
204204

205205
OpenTelemetry.execute(
206206
[:electric, :shape_db, :pool, :checkout],
207-
%{queue_time_μs: enqueued_duration_us},
207+
%{queue_time_us: enqueued_duration_us},
208208
%{label: label, stack_id: Keyword.get(pool_state, :stack_id)}
209209
)
210210

‎packages/sync-service/lib/electric/shapes/filter.ex‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ defmodule Electric.Shapes.Filter do
178178
@spec affected_shapes(Filter.t(), Changes.change() | Relation.t()) ::
179179
MapSet.t(shape_id())
180180
def affected_shapes(%Filter{} = filter, change) do
181-
OpenTelemetry.timed_fun("filter.affected_shapes.duration_µs", fn ->
181+
OpenTelemetry.timed_fun("filter.affected_shapes.duration_us", fn ->
182182
try do
183183
shapes_affected_by_change(filter, change)
184184
catch

‎packages/sync-service/lib/electric/telemetry/open_telemetry.ex‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -302,16 +302,16 @@ defmodule Electric.Telemetry.OpenTelemetry do
302302
e.g.
303303
304304
```elixir
305-
OpenTelemetry.start_interval(:quick_sleep.duration_µs)
305+
OpenTelemetry.start_interval(:quick_sleep.duration_us)
306306
Process.sleep(1)
307-
OpenTelemetry.start_interval(:longer_sleep.duration_µs)
307+
OpenTelemetry.start_interval(:longer_sleep.duration_us)
308308
Process.sleep(2)
309-
OpenTelemetry.stop_and_save_intervals(total_attribute: "total_sleep_µs")
309+
OpenTelemetry.stop_and_save_intervals(total_attribute: "total_sleep_us")
310310
```
311311
will add the following attributes to the current span:
312-
quick_sleep.duration_µs: 1000
313-
longer_sleep.duration_µs: 2000
314-
total_sleep_µs: 3000
312+
quick_sleep.duration_us: 1000
313+
longer_sleep.duration_us: 2000
314+
total_sleep_us: 3000
315315
"""
316316
@spec start_interval(atom()) :: :ok
317317
def start_interval(interval_name) when is_atom(interval_name) do

0 commit comments

Comments
 (0)