Skip to content
Merged
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
12 changes: 12 additions & 0 deletions cmd/snowglobe/scenarios.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ func failedPaymentFlow(ctx context.Context) {
trace.WithAttributes(
attribute.String("db.system.name", "postgresql"),
attribute.String("db.operation.name", "INSERT"),
attribute.String("db.namespace", "orders_db"),
),
)
sleep(5, 15)
Expand Down Expand Up @@ -467,6 +468,8 @@ func failedPaymentFlow(ctx context.Context) {
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(
attribute.String("http.request.method", "POST"),
attribute.String("server.address", "api.stripe.com"),
attribute.Int("server.port", 443),
attribute.String("service.peer.name", "stripe-api"),
attribute.Int("http.response.status_code", 402),
attribute.String("error.type", "card_declined"),
Expand Down Expand Up @@ -535,6 +538,7 @@ func bulkNotificationFlow(ctx context.Context) {
trace.WithAttributes(
attribute.String("db.system.name", "postgresql"),
attribute.String("db.operation.name", "SELECT"),
attribute.String("db.namespace", "orders_db"),
attribute.String("db.query.text", "SELECT * FROM notifications WHERE status = 'pending' LIMIT 100"),
),
)
Expand Down Expand Up @@ -1059,6 +1063,7 @@ func recommendationFlow(ctx context.Context) {
trace.WithAttributes(
attribute.String("db.system.name", "postgresql"),
attribute.String("db.operation.name", "SELECT"),
attribute.String("db.namespace", "inventory_db"),
attribute.Int("batch.size", 20+rand.Intn(30)),
),
)
Expand Down Expand Up @@ -1690,6 +1695,8 @@ func shippingUpdateFlow(ctx context.Context) {
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(
attribute.String("http.request.method", "POST"),
attribute.String("server.address", "api.sendgrid.com"),
attribute.Int("server.port", 443),
attribute.String("service.peer.name", "sendgrid"),
attribute.String("email.template", "shipping_update"),
),
Expand Down Expand Up @@ -1833,6 +1840,8 @@ func sagaCompensationFlow(ctx context.Context) {
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(
attribute.String("http.request.method", "POST"),
attribute.String("server.address", "api.stripe.com"),
attribute.Int("server.port", 443),
attribute.String("service.peer.name", "stripe-api"),
attribute.Int("http.response.status_code", 402),
attribute.Int("retry.attempt", attempt),
Expand Down Expand Up @@ -1955,6 +1964,9 @@ func sagaCompensationFlow(ctx context.Context) {
_, email := tracer("email-service").Start(ctx4, "SendEmail",
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(
attribute.String("http.request.method", "POST"),
attribute.String("server.address", "api.sendgrid.com"),
attribute.Int("server.port", 443),
attribute.String("service.peer.name", "sendgrid"),
attribute.String("email.template", "order_canceled"),
),
Expand Down
2 changes: 2 additions & 0 deletions cmd/snowglobe/scenarios_ai.go
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,8 @@ func returnRefundFlow(ctx context.Context) {
trace.WithSpanKind(trace.SpanKindClient),
trace.WithAttributes(
attribute.String("http.request.method", "POST"),
attribute.String("server.address", "api.sendgrid.com"),
attribute.Int("server.port", 443),
attribute.String("service.peer.name", "sendgrid"),
attribute.String("email.template", "refund_confirmation"),
),
Expand Down
47 changes: 47 additions & 0 deletions cmd/snowglobe/semconv_client_peer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package main

import (
"testing"

apitrace "go.opentelemetry.io/otel/trace"
)

// A CLIENT span names what it called. Without the attribute that identifies the callee, a backend can only
// key the dependency on the bare system or peer name, so the grid grows a second node for the same thing:
//
// - postgresql spans (db-spans): db.namespace is Conditionally Required "if available", and the generator
// always knows which database a flow uses. The grid runs several postgresql databases, so without it a
// call keys as plain "postgresql", beside "postgresql-orders_db" and the rest. Systems the grid runs as
// a single instance (redis, the search and vector stores) do not split, so they are not checked here.
// - outbound calls that name a peer (service.peer.name): server.address is Required on an HTTP client
// span (http-spans), and server.port is Conditionally Required once server.address is set. A bare
// single-label peer name such as "sendgrid" cannot be told apart from an internal service, so a
// backend that never sees it answer reads it as missing.

func TestClientSpansIdentifyTheirCallee(t *testing.T) {
spans := recordedSpans(t)

var dbClients, peerClients int
violations := map[string]int{}
for _, s := range spans {
if s.SpanKind() != apitrace.SpanKindClient {
continue
}
if sys, ok := attrOf(s, "db.system.name"); ok && sys.AsString() == "postgresql" {
dbClients++
for _, m := range missing(s, "db.namespace") {
violations["CLIENT "+s.Name()+" missing "+m]++
}
}
if _, ok := attrOf(s, "service.peer.name"); ok {
peerClients++
for _, m := range missing(s, "server.address", "server.port") {
violations["CLIENT "+s.Name()+" missing "+m+" (service.peer.name set)"]++
}
}
}
if dbClients == 0 || peerClients == 0 {
t.Fatalf("recorded %d database and %d peer-naming client spans: the harness is not driving the flows", dbClients, peerClients)
}
reportViolations(t, violations, "callee-identifying attribute")
}
Loading