(f func(database.Store) error, options *database.TxOptions)
| 70 | } |
| 71 | |
| 72 | func (m metricsStore) InTx(f func(database.Store) error, options *database.TxOptions) error { |
| 73 | if options == nil { |
| 74 | options = database.DefaultTXOptions() |
| 75 | } |
| 76 | |
| 77 | if options.TxIdentifier == "" { |
| 78 | // empty strings are hard to deal with in grafana |
| 79 | options.TxIdentifier = "unlabeled" |
| 80 | } |
| 81 | |
| 82 | start := time.Now() |
| 83 | err := m.Store.InTx(f, options) |
| 84 | dur := time.Since(start) |
| 85 | // The number of unique label combinations is |
| 86 | // 2 x #IDs x #of buckets |
| 87 | // So IDs should be used sparingly to prevent too much bloat. |
| 88 | m.txDuration.With(prometheus.Labels{ |
| 89 | "success": strconv.FormatBool(err == nil), |
| 90 | "tx_id": options.TxIdentifier, |
| 91 | }).Observe(dur.Seconds()) |
| 92 | |
| 93 | m.txRetries.With(prometheus.Labels{ |
| 94 | "success": strconv.FormatBool(err == nil), |
| 95 | "retries": strconv.FormatInt(int64(options.ExecutionCount()-1), 10), |
| 96 | "tx_id": options.TxIdentifier, |
| 97 | }).Inc() |
| 98 | |
| 99 | // Log all serializable transactions that are retried. |
| 100 | // This is expected to happen in production, but should be kept |
| 101 | // to a minimum. If these logs happen frequently, something is wrong. |
| 102 | if options.ExecutionCount() > 1 { |
| 103 | l := m.logger.Warn |
| 104 | if err != nil { |
| 105 | // Error level if retries were not enough |
| 106 | l = m.logger.Error |
| 107 | } |
| 108 | // No context is present in this function :( |
| 109 | l(context.Background(), "database transaction hit serialization error and had to retry", |
| 110 | slog.F("success", err == nil), // It can succeed on retry |
| 111 | // Note the error might not be a serialization error. It is possible |
| 112 | // the first error was a serialization error, and the error on the |
| 113 | // retry is different. If this is the case, we still want to log it |
| 114 | // since the first error was a serialization error. |
| 115 | slog.Error(err), // Might be nil, that is ok! |
| 116 | slog.F("executions", options.ExecutionCount()), |
| 117 | slog.F("tx_id", options.TxIdentifier), |
| 118 | slog.F("duration", dur), |
| 119 | ) |
| 120 | } |
| 121 | return err |
| 122 | } |
nothing calls this directly
no test coverage detected