R2: stress harness + contour resource observability + early run #33
+2
-2
@@ -11,7 +11,7 @@ and prints a trip-report summary. It stays in the repo for repeats.
|
|||||||
(each with a confirmed email identity) + `--guest` guest accounts and an active
|
(each with a confirmed email identity) + `--guest` guest accounts and an active
|
||||||
`sessions` row per account, then hands the plaintext bearer tokens to the driver.
|
`sessions` row per account, then hands the plaintext bearer tokens to the driver.
|
||||||
Token hashes match `backend/internal/session` (`hex(sha256(token))`), so the seeded
|
Token hashes match `backend/internal/session` (`hex(sha256(token))`), so the seeded
|
||||||
sessions resolve. Every row is tagged with the `lt:` marker for cleanup.
|
sessions resolve. Every row carries a distinctive display-name marker for cleanup.
|
||||||
2. **Drive** (edge protocol over h2c): assembles real 2–4 player games via the
|
2. **Drive** (edge protocol over h2c): assembles real 2–4 player games via the
|
||||||
invitation flow (`invitation.create` → `invitation.accept`, no robots), then runs
|
invitation flow (`invitation.create` → `invitation.accept`, no robots), then runs
|
||||||
each player's turn loop — poll `game.state`, replay `game.history`, generate a legal
|
each player's turn loop — poll `game.state`, replay `game.history`, generate a legal
|
||||||
@@ -52,7 +52,7 @@ resource baseline from the Grafana **Scrabble — Resources** dashboard
|
|||||||
|
|
||||||
```
|
```
|
||||||
loadtest run [flags] seed, drive the ramp + hammer, print the report
|
loadtest run [flags] seed, drive the ramp + hammer, print the report
|
||||||
loadtest cleanup [flags] delete everything the harness seeded (matched by the lt: marker)
|
loadtest cleanup [flags] delete everything the harness seeded (matched by the display-name marker)
|
||||||
```
|
```
|
||||||
|
|
||||||
Key `run` flags (env in parentheses):
|
Key `run` flags (env in parentheses):
|
||||||
|
|||||||
@@ -201,7 +201,9 @@ func (d *Driver) secondaryOp(ctx context.Context, p seed.Account, g *Game, rng *
|
|||||||
c, _ := d.edge.CheckWord(ctx, p.Token, g.ID, []byte{0, 1, 2})
|
c, _ := d.edge.CheckWord(ctx, p.Token, g.ID, []byte{0, 1, 2})
|
||||||
d.rec.Record("game.check_word", c, time.Since(t0))
|
d.rec.Record("game.check_word", c, time.Since(t0))
|
||||||
case 3:
|
case 3:
|
||||||
c, _ := d.edge.DraftSave(ctx, p.Token, g.ID, `{"rack_order":[],"board_tiles":[]}`)
|
// rack_order is an opaque string and board_tiles a (here empty) array, per the
|
||||||
|
// backend draft DTO; a malformed shape is rejected as bad_request.
|
||||||
|
c, _ := d.edge.DraftSave(ctx, p.Token, g.ID, `{"rack_order":"","board_tiles":[]}`)
|
||||||
d.rec.Record("draft.save", c, time.Since(t0))
|
d.rec.Record("draft.save", c, time.Since(t0))
|
||||||
case 4:
|
case 4:
|
||||||
c, _ := d.edge.DraftGet(ctx, p.Token, g.ID)
|
c, _ := d.edge.DraftGet(ctx, p.Token, g.ID)
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Marker prefixes every display_name the harness writes. Cleanup matches on it, so
|
// Marker prefixes every display_name the harness writes. Cleanup matches on it, so
|
||||||
// the harness only ever deletes its own rows and never touches real accounts.
|
// the harness only ever deletes its own rows and never touches real accounts. It is
|
||||||
const Marker = "lt:"
|
// a distinctive, letters-only string so a profile.update can resend the seeded name
|
||||||
|
// through the editable-display-name validator (which forbids digits and colons).
|
||||||
|
const Marker = "Zzloadtest"
|
||||||
|
|
||||||
// Schema-qualified targets so the seeder does not depend on the connection's
|
// Schema-qualified targets so the seeder does not depend on the connection's
|
||||||
// search_path (the backend pins search_path=backend; we qualify explicitly).
|
// search_path (the backend pins search_path=backend; we qualify explicitly).
|
||||||
@@ -100,7 +102,9 @@ func (s *Seeder) Seed(ctx context.Context, nDurable, nGuest int) (*Pool, error)
|
|||||||
if guest {
|
if guest {
|
||||||
kind = "g"
|
kind = "g"
|
||||||
}
|
}
|
||||||
name := fmt.Sprintf("%s%s-%06d", Marker, kind, i)
|
// A letters-only display name (Marker + kind), valid per the editable-name
|
||||||
|
// validator; account_id, not the name, is the unique key, so duplicates are fine.
|
||||||
|
name := Marker + kind
|
||||||
acctRows = append(acctRows, []any{aid, name, guest, lang})
|
acctRows = append(acctRows, []any{aid, name, guest, lang})
|
||||||
sessRows = append(sessRows, []any{sid, aid, hash, "active"})
|
sessRows = append(sessRows, []any{sid, aid, hash, "active"})
|
||||||
if !guest {
|
if !guest {
|
||||||
|
|||||||
Reference in New Issue
Block a user