feat: publish durable queue and spool gauges
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
package spool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
rvboxv1 "github.com/rvbox/rvbox/gen/go/rvbox/v1"
|
||||
)
|
||||
|
||||
// Telemetry is the bounded durable-spool state exposed as client gauges. It
|
||||
// contains no command body, output, UUID, or client identity.
|
||||
type Telemetry struct {
|
||||
QueuedCommands uint64
|
||||
ChargedBytes uint64
|
||||
}
|
||||
|
||||
func (store *Store) Telemetry(ctx context.Context) (Telemetry, error) {
|
||||
if store == nil {
|
||||
return Telemetry{}, errors.New("client spool is unavailable")
|
||||
}
|
||||
store.mu.Lock()
|
||||
defer store.mu.Unlock()
|
||||
if store.db == nil {
|
||||
return Telemetry{}, errors.New("client spool is closed")
|
||||
}
|
||||
var result Telemetry
|
||||
if err := store.db.QueryRowContext(ctx, `SELECT count(*) FROM commands WHERE terminal = 0 AND phase IN (?, ?)`, uint32(rvboxv1.CommandLifecycle_COMMAND_QUEUED), uint32(rvboxv1.CommandLifecycle_COMMAND_DISPATCHED)).Scan(&result.QueuedCommands); err != nil {
|
||||
return Telemetry{}, err
|
||||
}
|
||||
if err := store.db.QueryRowContext(ctx, `SELECT client_total_charged_bytes FROM spool_counters WHERE singleton = 1`).Scan(&result.ChargedBytes); err != nil {
|
||||
return Telemetry{}, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package spool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTelemetryReportsQueuedDurableSpool_HP_OPS_07(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
store := openTestStore(t, ctx, filepath.Join(t.TempDir(), "spool"), DefaultTombstoneLimit)
|
||||
issue := testUUID(t, "019c46f1-1d02-7000-8000-0000000000d2")
|
||||
if _, err := store.AcceptCommand(ctx, testCommand(issue, []byte("telemetry")), time.Now().UTC()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
telemetry, err := store.Telemetry(ctx)
|
||||
if err != nil || telemetry.QueuedCommands != 1 || telemetry.ChargedBytes == 0 {
|
||||
t.Fatalf("telemetry = %#v, %v", telemetry, err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
rvboxv1 "github.com/rvbox/rvbox/gen/go/rvbox/v1"
|
||||
)
|
||||
|
||||
// Telemetry is the bounded operational state that the server publishes as
|
||||
// gauges. It deliberately contains no client or command identifier.
|
||||
type Telemetry struct {
|
||||
QueuedCommands uint64
|
||||
ChargedBytes uint64
|
||||
}
|
||||
|
||||
func (store *Store) Telemetry(ctx context.Context) (Telemetry, error) {
|
||||
if store == nil {
|
||||
return Telemetry{}, errors.New("server store is unavailable")
|
||||
}
|
||||
store.mu.Lock()
|
||||
defer store.mu.Unlock()
|
||||
if store.db == nil {
|
||||
return Telemetry{}, ErrStoreClosed
|
||||
}
|
||||
var result Telemetry
|
||||
if err := store.db.QueryRowContext(ctx, `SELECT count(*) FROM commands WHERE lifecycle = ?`, uint32(rvboxv1.CommandLifecycle_COMMAND_QUEUED)).Scan(&result.QueuedCommands); err != nil {
|
||||
return Telemetry{}, err
|
||||
}
|
||||
if err := store.db.QueryRowContext(ctx, `SELECT command_charged_bytes FROM storage_counters WHERE singleton = 1`).Scan(&result.ChargedBytes); err != nil {
|
||||
return Telemetry{}, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rvbox/rvbox/internal/domain"
|
||||
)
|
||||
|
||||
func TestTelemetryReportsQueuedDurableState_HP_OPS_06(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
persistence, err := Open(ctx, Options{DataDir: filepath.Join(t.TempDir(), "state"), BusyTimeout: time.Second})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = persistence.Close() })
|
||||
if _, err := persistence.RegisterClientSession(ctx, ClientRegistration{ClientID: "telemetry-client", Platform: 2, Architecture: "amd64", DaemonVersion: "test", DaemonCWD: `C:\`, SupportedShells: []byte{1}, ClientInstanceID: [16]byte{1}, SessionID: [16]byte{2}, ConnectedAt: time.Now().UTC()}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
issue, err := domain.ParseUUIDv7("019c46f1-1d02-7000-8000-0000000000d1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := persistence.QueueCommand(ctx, QueueCommandInput{IssueUUID: issue, ClientID: "telemetry-client", IssueTime: time.Now().UTC(), ReceiptTime: time.Now().UTC(), ImmutableSHA256: sha256.Sum256([]byte("telemetry")), ExecutionSpec: []byte("spec")}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
telemetry, err := persistence.Telemetry(ctx)
|
||||
if err != nil || telemetry.QueuedCommands != 1 || telemetry.ChargedBytes == 0 {
|
||||
t.Fatalf("telemetry = %#v, %v", telemetry, err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user