Skip to content

Commit c60ff89

Browse files
kyle-a-wongdhartunian
authored andcommitted
server: add txn bundle download; refactor handlers
Previously, handlers for statement diagnostic bundles were oddly placed in the gateway configuration, and their implementations were crowded into `admin.go`. This change moves the code into respective files containing status endpoint implementations (still in same package, unfortunately) in `statement_diagnostic_requests.go` and `transaction_diagnostic.requests.go`. Handlers are registered in `server_http.go` making them easier to find, and they use the go http mux to route instead of gRPC Gateway. We also implement a handler for download transaction diagnostic bundles. This handler constructs the internal statement bundles first, adding them to the transaction's zip file, and also includes the transaction's own bundle containing its full trace. The output of the new handler is a similar direct zip file download like the statement bundle. Epic: CRDB-53541 Resolves: CRDB-53547 Release note: None
1 parent 755c186 commit c60ff89

File tree

9 files changed

+441
-93
lines changed

9 files changed

+441
-93
lines changed

pkg/server/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ go_library(
330330
"//pkg/util/log/logmetrics",
331331
"//pkg/util/log/logpb",
332332
"//pkg/util/log/severity",
333+
"//pkg/util/memzipper",
333334
"//pkg/util/metric",
334335
"//pkg/util/mon",
335336
"//pkg/util/netutil",
@@ -377,7 +378,6 @@ go_library(
377378
"@com_github_google_pprof//profile",
378379
"@com_github_gorilla_mux//:mux",
379380
"@com_github_grpc_ecosystem_grpc_gateway//runtime:go_default_library",
380-
"@com_github_grpc_ecosystem_grpc_gateway//utilities:go_default_library",
381381
"@com_github_lib_pq//:pq",
382382
"@com_github_marusama_semaphore//:semaphore",
383383
"@com_github_nightlyone_lockfile//:lockfile",

pkg/server/admin.go

Lines changed: 0 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,9 @@
66
package server
77

88
import (
9-
"bytes"
109
"context"
1110
"encoding/json"
1211
"fmt"
13-
"io"
14-
"net/http"
1512
"sort"
1613
"strconv"
1714
"strings"
@@ -77,10 +74,8 @@ import (
7774
"github.com/cockroachdb/errors"
7875
"github.com/cockroachdb/redact"
7976
gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime"
80-
gwutil "github.com/grpc-ecosystem/grpc-gateway/utilities"
8177
"google.golang.org/grpc"
8278
"google.golang.org/grpc/codes"
83-
"google.golang.org/grpc/metadata"
8479
grpcstatus "google.golang.org/grpc/status"
8580
"storj.io/drpc"
8681
"storj.io/drpc/drpcerr"
@@ -280,44 +275,6 @@ func (s *adminServer) RegisterDRPCService(d drpc.Mux) error {
280275
func (s *adminServer) RegisterGateway(
281276
ctx context.Context, mux *gwruntime.ServeMux, conn *grpc.ClientConn,
282277
) error {
283-
// Register the /_admin/v1/stmtbundle endpoint, which serves statement support
284-
// bundles as files.
285-
stmtBundlePattern := gwruntime.MustPattern(gwruntime.NewPattern(
286-
1, /* version */
287-
[]int{
288-
int(gwutil.OpLitPush), 0, int(gwutil.OpLitPush), 1, int(gwutil.OpLitPush), 2,
289-
int(gwutil.OpPush), 0, int(gwutil.OpConcatN), 1, int(gwutil.OpCapture), 3},
290-
[]string{"_admin", "v1", "stmtbundle", "id"},
291-
"", /* verb */
292-
))
293-
294-
mux.Handle("GET", stmtBundlePattern, func(
295-
w http.ResponseWriter, req *http.Request, pathParams map[string]string,
296-
) {
297-
idStr, ok := pathParams["id"]
298-
if !ok {
299-
http.Error(w, "missing id", http.StatusBadRequest)
300-
return
301-
}
302-
id, err := strconv.ParseInt(idStr, 10, 64)
303-
if err != nil {
304-
http.Error(w, "invalid id", http.StatusBadRequest)
305-
return
306-
}
307-
308-
// The privilege checks in the privilege checker below checks the user in the incoming
309-
// gRPC metadata.
310-
md := authserver.TranslateHTTPAuthInfoToGRPCMetadata(req.Context(), req)
311-
authCtx := metadata.NewIncomingContext(req.Context(), md)
312-
authCtx = s.AnnotateCtx(authCtx)
313-
if err := s.privilegeChecker.RequireViewActivityAndNoViewActivityRedactedPermission(authCtx); err != nil {
314-
http.Error(w, err.Error(), http.StatusForbidden)
315-
return
316-
}
317-
s.getStatementBundle(req.Context(), id, w)
318-
})
319-
320-
// Register the endpoints defined in the proto.
321278
return serverpb.RegisterAdminHandler(ctx, mux, conn)
322279
}
323280

@@ -2646,55 +2603,6 @@ func (s *adminServer) QueryPlan(
26462603
}, nil
26472604
}
26482605

2649-
// getStatementBundle retrieves the statement bundle with the given id and
2650-
// writes it out as an attachment. Note this function assumes the user has
2651-
// permission to access the statement bundle.
2652-
func (s *adminServer) getStatementBundle(ctx context.Context, id int64, w http.ResponseWriter) {
2653-
row, err := s.internalExecutor.QueryRowEx(
2654-
ctx, "admin-stmt-bundle", nil, /* txn */
2655-
sessiondata.NodeUserSessionDataOverride,
2656-
"SELECT bundle_chunks FROM system.statement_diagnostics WHERE id=$1 AND bundle_chunks IS NOT NULL",
2657-
id,
2658-
)
2659-
if err != nil {
2660-
http.Error(w, err.Error(), http.StatusInternalServerError)
2661-
return
2662-
}
2663-
if row == nil {
2664-
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
2665-
return
2666-
}
2667-
// Put together the entire bundle. Ideally we would stream it in chunks,
2668-
// but it's hard to return errors once we start.
2669-
var bundle bytes.Buffer
2670-
chunkIDs := row[0].(*tree.DArray).Array
2671-
for _, chunkID := range chunkIDs {
2672-
chunkRow, err := s.internalExecutor.QueryRowEx(
2673-
ctx, "admin-stmt-bundle", nil, /* txn */
2674-
sessiondata.NodeUserSessionDataOverride,
2675-
"SELECT data FROM system.statement_bundle_chunks WHERE id=$1",
2676-
chunkID,
2677-
)
2678-
if err != nil {
2679-
http.Error(w, err.Error(), http.StatusInternalServerError)
2680-
return
2681-
}
2682-
if chunkRow == nil {
2683-
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
2684-
return
2685-
}
2686-
data := chunkRow[0].(*tree.DBytes)
2687-
bundle.WriteString(string(*data))
2688-
}
2689-
2690-
w.Header().Set(
2691-
"Content-Disposition",
2692-
fmt.Sprintf("attachment; filename=stmt-bundle-%d.zip", id),
2693-
)
2694-
2695-
_, _ = io.Copy(w, &bundle)
2696-
}
2697-
26982606
// DecommissionPreCheck runs checks and returns the DecommissionPreCheckResponse
26992607
// for the given nodes.
27002608
func (s *systemAdminServer) DecommissionPreCheck(

pkg/server/apiconstants/constants.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ const (
1717
// (This is also aliased via /health.)
1818
AdminHealth = AdminPrefix + "health"
1919

20+
// AdminStmtBundle is the URL at which we download statement bundles.
21+
// It does not share the admin handler because the implementation
22+
// downloads zip files, not gRPC responses.
23+
AdminStmtBundle = AdminPrefix + "stmtbundle/{id}"
24+
25+
// AdminTxnBundle is the URL at which we download transaction bundles.
26+
// It does not share the admin handler because the implementation
27+
// downloads zip files, not gRPC responses.
28+
AdminTxnBundle = AdminPrefix + "txnbundle/{id}"
29+
2030
// StatusPrefix is the root of the cluster statistics and metrics API.
2131
StatusPrefix = "/_status/"
2232

0 commit comments

Comments
 (0)