Skip to content

Commit aed749a

Browse files
craig[bot]jordanlewis
andcommitted
Merge #148575
148575: cli: add cost estimate and success logging to tsdump upload r=jordanlewis a=jordanlewis This PR adds a cost estimate to the tsdump Datadog upload command to help people understand how much a tsdump upload costs (roughly $.20 per single-node upload, $2 per 10-node upload, or $20 per 100-node upload). It also adds structured success logging so that it's possible to see historical information about the uploads that were sent to Datadog. Epic: none Release note: None Co-authored-by: Jordan Lewis <[email protected]>
2 parents 8917e45 + 068f386 commit aed749a

File tree

8 files changed

+233
-223
lines changed

8 files changed

+233
-223
lines changed

pkg/cli/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@ go_library(
256256
"@com_github_cockroachdb_pebble//vfs",
257257
"@com_github_cockroachdb_redact//:redact",
258258
"@com_github_cockroachdb_ttycolor//:ttycolor",
259+
"@com_github_datadog_datadog_api_client_go_v2//api/datadog",
260+
"@com_github_datadog_datadog_api_client_go_v2//api/datadogV2",
259261
"@com_github_dustin_go_humanize//:go-humanize",
260262
"@com_github_fsnotify_fsnotify//:fsnotify",
261263
"@com_github_gogo_protobuf//jsonpb",
@@ -467,6 +469,7 @@ go_test(
467469
"@com_github_cockroachdb_errors//:errors",
468470
"@com_github_cockroachdb_errors//oserror",
469471
"@com_github_cockroachdb_pebble//vfs",
472+
"@com_github_datadog_datadog_api_client_go_v2//api/datadogV2",
470473
"@com_github_google_pprof//profile",
471474
"@com_github_pmezard_go_difflib//difflib",
472475
"@com_github_spf13_cobra//:cobra",

pkg/cli/debug.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,6 +1615,7 @@ func init() {
16151615
f.StringVar(&debugTimeSeriesDumpOpts.organizationName, "org-name", "", "organization name to use in datadog upload")
16161616
f.StringVar(&debugTimeSeriesDumpOpts.userName, "user-name", "", "name of the user to perform datadog upload")
16171617
f.StringVar(&debugTimeSeriesDumpOpts.storeToNodeMapYAMLFile, "store-to-node-map-file", "", "yaml file path which contains the mapping of store ID to node ID for datadog upload.")
1618+
f.BoolVar(&debugTimeSeriesDumpOpts.dryRun, "dry-run", false, "run in dry-run mode without making any actual uploads")
16181619

16191620
f = debugSendKVBatchCmd.Flags()
16201621
f.StringVar(&debugSendKVBatchContext.traceFormat, "trace", debugSendKVBatchContext.traceFormat,

pkg/cli/testdata/tsdump/json

Lines changed: 34 additions & 34 deletions
Large diffs are not rendered by default.

pkg/cli/testdata/tsdump_upload_e2e

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,9 @@ cr.node.sql.query.count,2021-01-01T00:00:00Z,1,100.5
55
cr.node.sql.query.count,2021-01-01T00:00:10Z,1,102.3
66
cr.store.rocksdb.block.cache.usage,2021-01-01T00:00:00Z,2,75.2
77
----
8-
{"series":[{"metric":"crdb.tsdump.admission.admitted.elastic-cpu","type":0,"points":[{"timestamp":1748248320,"value":1}],"interval":10,"resources":null,"tags":["node_id:1","cluster_type:SELF_HOSTED","cluster_label:\"test-cluster\"","cluster_id:test-cluster-id","zendesk_ticket:zd-test","org_name:test-org","user_name:test-user","upload_id:\"test-cluster\"-20241114000000","upload_timestamp:2024-11-14 00:00:00","upload_year:2024","upload_month:11","upload_day:14"]},{"metric":"crdb.tsdump.sql.query.count","type":0,"points":[{"timestamp":1609459200,"value":100.5}],"interval":10,"resources":null,"tags":["node_id:1","cluster_type:SELF_HOSTED","cluster_label:\"test-cluster\"","cluster_id:test-cluster-id","zendesk_ticket:zd-test","org_name:test-org","user_name:test-user","upload_id:\"test-cluster\"-20241114000000","upload_timestamp:2024-11-14 00:00:00","upload_year:2024","upload_month:11","upload_day:14"]},{"metric":"crdb.tsdump.sql.query.count","type":0,"points":[{"timestamp":1609459210,"value":102.3}],"interval":10,"resources":null,"tags":["node_id:1","cluster_type:SELF_HOSTED","cluster_label:\"test-cluster\"","cluster_id:test-cluster-id","zendesk_ticket:zd-test","org_name:test-org","user_name:test-user","upload_id:\"test-cluster\"-20241114000000","upload_timestamp:2024-11-14 00:00:00","upload_year:2024","upload_month:11","upload_day:14"]},{"metric":"crdb.tsdump.rocksdb.block.cache.usage","type":0,"points":[{"timestamp":1609459200,"value":75.2}],"interval":10,"resources":null,"tags":["store:2","cluster_type:SELF_HOSTED","cluster_label:\"test-cluster\"","cluster_id:test-cluster-id","zendesk_ticket:zd-test","org_name:test-org","user_name:test-user","upload_id:\"test-cluster\"-20241114000000","upload_timestamp:2024-11-14 00:00:00","upload_year:2024","upload_month:11","upload_day:14"]}]}
8+
----
9+
{"series":[{"interval":10,"metric":"crdb.tsdump.admission.admitted.elastic-cpu","points":[{"timestamp":1748248320,"value":1}],"tags":["node_id:1","cluster_type:SELF_HOSTED","cluster_label:\"test-cluster\"","cluster_id:test-cluster-id","zendesk_ticket:zd-test","org_name:test-org","user_name:test-user","upload_id:\"test-cluster\"-20241114000000","upload_timestamp:2024-11-14 00:00:00","upload_year:2024","upload_month:11","upload_day:14"],"type":0},{"interval":10,"metric":"crdb.tsdump.sql.query.count","points":[{"timestamp":1609459200,"value":100.5}],"tags":["node_id:1","cluster_type:SELF_HOSTED","cluster_label:\"test-cluster\"","cluster_id:test-cluster-id","zendesk_ticket:zd-test","org_name:test-org","user_name:test-user","upload_id:\"test-cluster\"-20241114000000","upload_timestamp:2024-11-14 00:00:00","upload_year:2024","upload_month:11","upload_day:14"],"type":0},{"interval":10,"metric":"crdb.tsdump.sql.query.count","points":[{"timestamp":1609459210,"value":102.3}],"tags":["node_id:1","cluster_type:SELF_HOSTED","cluster_label:\"test-cluster\"","cluster_id:test-cluster-id","zendesk_ticket:zd-test","org_name:test-org","user_name:test-user","upload_id:\"test-cluster\"-20241114000000","upload_timestamp:2024-11-14 00:00:00","upload_year:2024","upload_month:11","upload_day:14"],"type":0},{"interval":10,"metric":"crdb.tsdump.rocksdb.block.cache.usage","points":[{"timestamp":1609459200,"value":75.2}],"tags":["store:2","cluster_type:SELF_HOSTED","cluster_label:\"test-cluster\"","cluster_id:test-cluster-id","zendesk_ticket:zd-test","org_name:test-org","user_name:test-user","upload_id:\"test-cluster\"-20241114000000","upload_timestamp:2024-11-14 00:00:00","upload_year:2024","upload_month:11","upload_day:14"],"type":0}]}
10+
11+
[{"ddsource":"tsdump_upload","ddtags":"cluster_type:SELF_HOSTED,cluster_label:\"test-cluster\",cluster_id:test-cluster-id,zendesk_ticket:zd-test,org_name:test-org,user_name:test-user,upload_id:\"test-cluster\"-20241114000000,upload_timestamp:2024-11-14 00:00:00,upload_year:2024,upload_month:11,upload_day:14,series_uploaded:4","hostname":"hostname","message":"{\"message\":\"tsdump upload completed: uploaded 4 series overall\",\"series_uploaded\":4,\"estimated_cost\":0.00024931506849315067,\"duration\":0,\"dry_run\":false,\"success\":true}","service":"tsdump_upload"}]
12+
----
13+
----

pkg/cli/tsdump.go

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ var debugTimeSeriesDumpOpts = struct {
5151
organizationName string
5252
userName string
5353
storeToNodeMapYAMLFile string
54+
dryRun bool
5455
}{
5556
format: tsDumpText,
5657
from: timestampValue{},
@@ -59,6 +60,9 @@ var debugTimeSeriesDumpOpts = struct {
5960
yaml: "/tmp/tsdump.yaml",
6061
}
6162

63+
// hostNameOverride is used to override the hostname for testing purpose.
64+
var hostNameOverride string
65+
6266
var debugTimeSeriesDumpCmd = &cobra.Command{
6367
Use: "tsdump",
6468
Short: "dump all the raw timeseries values in a cluster",
@@ -111,18 +115,16 @@ will then convert it to the --format requested in the current invocation.
111115
return errors.New("no input file provided")
112116
}
113117

114-
targetURL, err := getDatadogTargetURL(debugTimeSeriesDumpOpts.ddSite)
115-
if err != nil {
116-
return err
117-
}
118-
119-
var datadogWriter = makeDatadogWriter(
120-
targetURL,
118+
datadogWriter, err := makeDatadogWriter(
119+
debugTimeSeriesDumpOpts.ddSite,
121120
cmd == tsDumpDatadogInit,
122121
debugTimeSeriesDumpOpts.ddApiKey,
123122
100,
124-
doDDRequest,
123+
hostNameOverride,
125124
)
125+
if err != nil {
126+
return err
127+
}
126128
return datadogWriter.upload(args[0])
127129
case tsDumpOpenMetrics:
128130
if debugTimeSeriesDumpOpts.targetURL != "" {
@@ -259,15 +261,6 @@ will then convert it to the --format requested in the current invocation.
259261
}),
260262
}
261263

262-
func getDatadogTargetURL(site string) (string, error) {
263-
host, ok := ddSiteToHostMap[site]
264-
if !ok {
265-
return "", fmt.Errorf("unsupported datadog site '%s'", site)
266-
}
267-
targetURL := fmt.Sprintf(targetURLFormat, host)
268-
return targetURL, nil
269-
}
270-
271264
func doRequest(req *http.Request) error {
272265
resp, err := http.DefaultClient.Do(req)
273266
if err != nil {

pkg/cli/tsdump_test.go

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ import (
1313
"math/rand"
1414
"net"
1515
"net/http"
16+
"net/http/httptest"
1617
"os"
1718
"strconv"
1819
"strings"
1920
"testing"
2021
"time"
2122

23+
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV2"
2224
"github.com/cockroachdb/cockroach/pkg/testutils"
2325
"github.com/cockroachdb/cockroach/pkg/ts/tspb"
2426
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
@@ -145,7 +147,7 @@ func parseTSInput(t *testing.T, input string, w tsWriter) {
145147
}
146148

147149
func parseDDInput(t *testing.T, input string, w *datadogWriter) {
148-
var data *DatadogSeries
150+
var data *datadogV2.MetricSeries
149151
var source, storeNodeKey string
150152

151153
for _, s := range strings.Split(input, "\n") {
@@ -164,10 +166,10 @@ func parseDDInput(t *testing.T, input string, w *datadogWriter) {
164166
(data != nil && data.Metric != metricName ||
165167
(data != nil && source != nameValueTimestamp[1])) {
166168
if data != nil {
167-
_, err := w.emitDataDogMetrics([]DatadogSeries{*data})
169+
_, err := w.emitDataDogMetrics([]datadogV2.MetricSeries{*data})
168170
require.NoError(t, err)
169171
}
170-
data = &DatadogSeries{
172+
data = &datadogV2.MetricSeries{
171173
Metric: metricName,
172174
Type: w.resolveMetricType(metricName),
173175
}
@@ -178,12 +180,12 @@ func parseDDInput(t *testing.T, input string, w *datadogWriter) {
178180
require.NoError(t, err)
179181
ts, err := strconv.ParseInt(nameValueTimestamp[3], 10, 64)
180182
require.NoError(t, err)
181-
data.Points = append(data.Points, DatadogPoint{
182-
Value: value,
183-
Timestamp: ts,
183+
data.Points = append(data.Points, datadogV2.MetricPoint{
184+
Value: &value,
185+
Timestamp: &ts,
184186
})
185187
}
186-
_, err := w.emitDataDogMetrics([]DatadogSeries{*data})
188+
_, err := w.emitDataDogMetrics([]datadogV2.MetricSeries{*data})
187189
require.NoError(t, err)
188190
}
189191

@@ -193,7 +195,17 @@ func TestTsDumpFormatsDataDriven(t *testing.T) {
193195
defer testutils.TestingHook(&getCurrentTime, func() time.Time {
194196
return time.Date(2024, 11, 14, 0, 0, 0, 0, time.UTC)
195197
})()
196-
198+
var testReqs []*http.Request
199+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
200+
r2 := r.Clone(r.Context())
201+
// Clone the body so that it can be read again
202+
body, err := io.ReadAll(r.Body)
203+
require.NoError(t, err)
204+
r2.Body = io.NopCloser(bytes.NewReader(body))
205+
testReqs = append(testReqs, r2)
206+
w.WriteHeader(http.StatusOK)
207+
}))
208+
defer server.Close()
197209
datadriven.Walk(t, "testdata/tsdump", func(t *testing.T, path string) {
198210
datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string {
199211
var w tsWriter
@@ -204,28 +216,25 @@ func TestTsDumpFormatsDataDriven(t *testing.T) {
204216
debugTimeSeriesDumpOpts.zendeskTicket = "zd-test"
205217
debugTimeSeriesDumpOpts.organizationName = "test-org"
206218
debugTimeSeriesDumpOpts.userName = "test-user"
207-
var testReqs []*http.Request
208219
var series int
209220
d.ScanArgs(t, "series-threshold", &series)
210-
var ddwriter = makeDatadogWriter(
211-
"https://example.com/data", d.Cmd == "format-datadog-init", "api-key", series, func(req *http.Request,
212-
) error {
213-
testReqs = append(testReqs, req)
214-
return nil
215-
})
221+
var ddwriter, err = makeDatadogWriter(
222+
defaultDDSite, d.Cmd == "format-datadog-init", "api-key", series,
223+
server.Listener.Addr().String(),
224+
)
225+
require.NoError(t, err)
216226

217227
parseDDInput(t, d.Input, ddwriter)
218228

219229
out := strings.Builder{}
220230
for _, tr := range testReqs {
221-
rc, err := tr.GetBody()
222-
require.NoError(t, err)
223-
zipR, err := gzip.NewReader(rc)
231+
reader, err := gzip.NewReader(tr.Body)
224232
require.NoError(t, err)
225-
body, err := io.ReadAll(zipR)
233+
body, err := io.ReadAll(reader)
226234
require.NoError(t, err)
227235
out.WriteString(fmt.Sprintf("%s: %s\nDD-API-KEY: %s\nBody: %s", tr.Method, tr.URL, tr.Header.Get("DD-API-KEY"), body))
228236
}
237+
testReqs = testReqs[:0] // reset the slice
229238
return out.String()
230239
case "format-json":
231240
debugTimeSeriesDumpOpts.clusterLabel = "test-cluster"

0 commit comments

Comments
 (0)