Skip to content

Commit e7be931

Browse files
committed
add deduplication attribute to metrics
1 parent 350963d commit e7be931

File tree

2 files changed

+36
-27
lines changed

2 files changed

+36
-27
lines changed

internal/ct/handlers.go

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/transparency-dev/tesseract/internal/types/tls"
3232
"github.com/transparency-dev/tesseract/internal/x509util"
3333
tessera "github.com/transparency-dev/trillian-tessera"
34+
"go.opentelemetry.io/otel/attribute"
3435
"go.opentelemetry.io/otel/metric"
3536
"k8s.io/klog/v2"
3637
)
@@ -108,25 +109,25 @@ type pathHandlers map[string]appHandler
108109
type appHandler struct {
109110
log *log
110111
opts *HandlerOptions
111-
handler func(context.Context, *HandlerOptions, *log, http.ResponseWriter, *http.Request) (int, error)
112+
handler func(context.Context, *HandlerOptions, *log, http.ResponseWriter, *http.Request) (int, []attribute.KeyValue, error)
112113
name entrypointName
113114
method string // http.MethodGet or http.MethodPost
114115
}
115116

116117
// ServeHTTP for an AppHandler invokes the underlying handler function but
117118
// does additional common error and stats processing.
118119
func (a appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
119-
var statusCode int
120-
121120
originAttr := originKey.String(a.log.origin)
122121
operationAttr := operationKey.String(a.name)
123-
reqCounter.Add(r.Context(), 1, metric.WithAttributes(originAttr, operationAttr))
122+
attrs := []attribute.KeyValue{originAttr, operationAttr}
123+
124+
reqCounter.Add(r.Context(), 1, metric.WithAttributes(attrs...))
124125
startTime := time.Now()
125126
logCtx := a.opts.RequestLog.start(r.Context())
126127
a.opts.RequestLog.origin(logCtx, a.log.origin)
127128
defer func() {
128129
latency := time.Since(startTime).Seconds()
129-
reqDuration.Record(r.Context(), latency, metric.WithAttributes(originAttr, operationAttr, codeKey.Int(statusCode)))
130+
reqDuration.Record(r.Context(), latency, metric.WithAttributes(attrs...))
130131
}()
131132

132133
klog.V(2).Infof("%s: request %v %q => %s", a.log.origin, r.Method, r.URL, a.name)
@@ -153,11 +154,13 @@ func (a appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
153154
ctx, cancel := context.WithTimeout(logCtx, a.opts.Deadline)
154155
defer cancel()
155156

156-
var err error
157-
statusCode, err = a.handler(ctx, a.opts, a.log, w, r)
157+
statusCode, hattrs, err := a.handler(ctx, a.opts, a.log, w, r)
158+
for _, attr := range hattrs {
159+
attrs = append(attrs, attr)
160+
}
158161
a.opts.RequestLog.status(ctx, statusCode)
159162
klog.V(2).Infof("%s: %s <= st=%d", a.log.origin, a.name, statusCode)
160-
rspCounter.Add(r.Context(), 1, metric.WithAttributes(originAttr, operationAttr, codeKey.Int(statusCode)))
163+
rspCounter.Add(r.Context(), 1, metric.WithAttributes(attrs...))
161164
if err != nil {
162165
klog.Warningf("%s: %s handler error: %v", a.log.origin, a.name, err)
163166
a.opts.sendHTTPError(w, statusCode, err)
@@ -240,7 +243,7 @@ func parseBodyAsJSONChain(r *http.Request) (rfc6962.AddChainRequest, error) {
240243

241244
// addChainInternal is called by add-chain and add-pre-chain as the logic involved in
242245
// processing these requests is almost identical
243-
func addChainInternal(ctx context.Context, opts *HandlerOptions, log *log, w http.ResponseWriter, r *http.Request, isPrecert bool) (int, error) {
246+
func addChainInternal(ctx context.Context, opts *HandlerOptions, log *log, w http.ResponseWriter, r *http.Request, isPrecert bool) (int, []attribute.KeyValue, error) {
244247
var method entrypointName
245248
if isPrecert {
246249
method = addPreChainName
@@ -251,15 +254,15 @@ func addChainInternal(ctx context.Context, opts *HandlerOptions, log *log, w htt
251254
// Check the contents of the request and convert to slice of certificates.
252255
addChainReq, err := parseBodyAsJSONChain(r)
253256
if err != nil {
254-
return http.StatusBadRequest, fmt.Errorf("%s: failed to parse add-chain body: %s", log.origin, err)
257+
return http.StatusBadRequest, nil, fmt.Errorf("%s: failed to parse add-chain body: %s", log.origin, err)
255258
}
256259
// Log the DERs now because they might not parse as valid X.509.
257260
for _, der := range addChainReq.Chain {
258261
opts.RequestLog.addDERToChain(ctx, der)
259262
}
260263
chain, err := log.chainValidator.Validate(addChainReq, isPrecert)
261264
if err != nil {
262-
return http.StatusBadRequest, fmt.Errorf("failed to verify add-chain contents: %s", err)
265+
return http.StatusBadRequest, nil, fmt.Errorf("failed to verify add-chain contents: %s", err)
263266
}
264267
for _, cert := range chain {
265268
opts.RequestLog.addCertToChain(ctx, cert)
@@ -271,74 +274,79 @@ func addChainInternal(ctx context.Context, opts *HandlerOptions, log *log, w htt
271274

272275
entry, err := x509util.EntryFromChain(chain, isPrecert, timeMillis)
273276
if err != nil {
274-
return http.StatusBadRequest, fmt.Errorf("failed to build MerkleTreeLeaf: %s", err)
277+
return http.StatusBadRequest, nil, fmt.Errorf("failed to build MerkleTreeLeaf: %s", err)
275278
}
276279

277280
if err := log.storage.AddIssuerChain(ctx, chain[1:]); err != nil {
278-
return http.StatusInternalServerError, fmt.Errorf("failed to store issuer chain: %s", err)
281+
return http.StatusInternalServerError, nil, fmt.Errorf("failed to store issuer chain: %s", err)
279282
}
280283

281284
klog.V(2).Infof("%s: %s => storage.Add", log.origin, method)
282285
index, dedupedTimeMillis, err := log.storage.Add(ctx, entry)
283286
if err != nil {
284287
if errors.Is(err, tessera.ErrPushback) {
285288
w.Header().Add("Retry-After", "1")
286-
return http.StatusServiceUnavailable, fmt.Errorf("received pushback from Tessera sequencer: %v", err)
289+
return http.StatusServiceUnavailable, nil, fmt.Errorf("received pushback from Tessera sequencer: %v", err)
287290
}
288-
return http.StatusInternalServerError, fmt.Errorf("couldn't store the leaf: %v", err)
291+
return http.StatusInternalServerError, nil, fmt.Errorf("couldn't store the leaf: %v", err)
292+
}
293+
dedupedAttribute := dedupedKey.Bool(false)
294+
isDup := dedupedTimeMillis != timeMillis
295+
if isDup {
296+
dedupedAttribute = dedupedKey.Bool(true)
289297
}
290298
entry.Timestamp = dedupedTimeMillis
291299

292300
// Always use the returned leaf as the basis for an SCT.
293301
var loggedLeaf rfc6962.MerkleTreeLeaf
294302
leafValue := entry.MerkleTreeLeaf(index)
295303
if rest, err := tls.Unmarshal(leafValue, &loggedLeaf); err != nil {
296-
return http.StatusInternalServerError, fmt.Errorf("failed to reconstruct MerkleTreeLeaf: %s", err)
304+
return http.StatusInternalServerError, nil, fmt.Errorf("failed to reconstruct MerkleTreeLeaf: %s", err)
297305
} else if len(rest) > 0 {
298-
return http.StatusInternalServerError, fmt.Errorf("extra data (%d bytes) on reconstructing MerkleTreeLeaf", len(rest))
306+
return http.StatusInternalServerError, nil, fmt.Errorf("extra data (%d bytes) on reconstructing MerkleTreeLeaf", len(rest))
299307
}
300308

301309
// As the Log server has definitely got the Merkle tree leaf, we can
302310
// generate an SCT and respond with it.
303311
sct, err := log.signSCT(&loggedLeaf)
304312
if err != nil {
305-
return http.StatusInternalServerError, fmt.Errorf("failed to generate SCT: %s", err)
313+
return http.StatusInternalServerError, nil, fmt.Errorf("failed to generate SCT: %s", err)
306314
}
307315
sctBytes, err := tls.Marshal(*sct)
308316
if err != nil {
309-
return http.StatusInternalServerError, fmt.Errorf("failed to marshall SCT: %s", err)
317+
return http.StatusInternalServerError, nil, fmt.Errorf("failed to marshall SCT: %s", err)
310318
}
311319
// We could possibly fail to issue the SCT after this but it's v. unlikely.
312320
opts.RequestLog.issueSCT(ctx, sctBytes)
313321
err = marshalAndWriteAddChainResponse(sct, w)
314322
if err != nil {
315323
// reason is logged and http status is already set
316-
return http.StatusInternalServerError, fmt.Errorf("failed to write response: %s", err)
324+
return http.StatusInternalServerError, nil, fmt.Errorf("failed to write response: %s", err)
317325
}
318326
klog.V(3).Infof("%s: %s <= SCT", log.origin, method)
319-
if sct.Timestamp == timeMillis {
327+
if !isDup {
320328
lastSCTTimestamp.Record(ctx, otel.Clamp64(sct.Timestamp), metric.WithAttributes(originKey.String(log.origin)))
321329
lastSCTIndex.Record(ctx, otel.Clamp64(index), metric.WithAttributes(originKey.String(log.origin)))
322330
}
323331

324-
return http.StatusOK, nil
332+
return http.StatusOK, []attribute.KeyValue{dedupedAttribute}, nil
325333
}
326334

327-
func addChain(ctx context.Context, opts *HandlerOptions, log *log, w http.ResponseWriter, r *http.Request) (int, error) {
335+
func addChain(ctx context.Context, opts *HandlerOptions, log *log, w http.ResponseWriter, r *http.Request) (int, []attribute.KeyValue, error) {
328336
ctx, span := tracer.Start(ctx, "tesseract.addChain")
329337
defer span.End()
330338

331339
return addChainInternal(ctx, opts, log, w, r, false)
332340
}
333341

334-
func addPreChain(ctx context.Context, opts *HandlerOptions, log *log, w http.ResponseWriter, r *http.Request) (int, error) {
342+
func addPreChain(ctx context.Context, opts *HandlerOptions, log *log, w http.ResponseWriter, r *http.Request) (int, []attribute.KeyValue, error) {
335343
ctx, span := tracer.Start(ctx, "tesseract.addPreChain")
336344
defer span.End()
337345

338346
return addChainInternal(ctx, opts, log, w, r, true)
339347
}
340348

341-
func getRoots(ctx context.Context, opts *HandlerOptions, log *log, w http.ResponseWriter, _ *http.Request) (int, error) {
349+
func getRoots(ctx context.Context, opts *HandlerOptions, log *log, w http.ResponseWriter, _ *http.Request) (int, []attribute.KeyValue, error) {
342350
_, span := tracer.Start(ctx, "tesseract.getRoots")
343351
defer span.End()
344352

@@ -355,10 +363,10 @@ func getRoots(ctx context.Context, opts *HandlerOptions, log *log, w http.Respon
355363
err := enc.Encode(jsonMap)
356364
if err != nil {
357365
klog.Warningf("%s: get_roots failed: %v", log.origin, err)
358-
return http.StatusInternalServerError, fmt.Errorf("get-roots failed with: %s", err)
366+
return http.StatusInternalServerError, nil, fmt.Errorf("get-roots failed with: %s", err)
359367
}
360368

361-
return http.StatusOK, nil
369+
return http.StatusOK, nil, nil
362370
}
363371

364372
// marshalAndWriteAddChainResponse is used by add-chain and add-pre-chain to create and write

internal/ct/otel.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ var (
3131
codeKey = attribute.Key("http.response.status_code")
3232
operationKey = attribute.Key("tesseract.operation")
3333
originKey = attribute.Key("tesseract.origin")
34+
dedupedKey = attribute.Key("tesseract.dedup")
3435
)
3536

3637
func mustCreate[T any](t T, err error) T {

0 commit comments

Comments
 (0)