Address Gadfly review on #19: batch fills + tighten Region
Build image / build-and-push (push) Successful in 15s
Build image / build-and-push (push) Successful in 15s
- FillRegion: insert the whole batch in one transaction (store.CreatePlantings) instead of one round-trip per plop, and refuse fills over maxFillPlops (5000) so a max-sized bed with tiny spacing can't generate ~10^5 sequential writes. Guards the computed radius is finite/positive and clamps the region to the object's bounds before packing (bounds hexCenters). - FillNamedRegion + FillRegion share a fillLoaded body, so the object is loaded and authorized once (no double objectForRole). - Region is now rect-only — dropped the speculative circle fields/branch that NamedRegion never produced and hexCenters didn't pack (circles are post-v1). - NamedRegion guards a nil object and no longer maps a blank name to "all" (blank → ErrInvalidInput, matching the doc). - ClearObject documents why it deliberately doesn't require plantable. Tests: empty/nil region name → ErrInvalidInput; an oversized fill → ErrInvalidInput (over the cap). Tagged demo tidied (checked errors, domain.RoleViewer, t.Helper). GOWORK=off go build/vet/test ./internal/... green; tagged agent test green against majordomo (go.mod stays majordomo-free). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
This commit is contained in:
@@ -132,6 +132,36 @@ func (d *DB) CreatePlanting(ctx context.Context, p *domain.Planting) (*domain.Pl
|
||||
return created, nil
|
||||
}
|
||||
|
||||
// CreatePlantings inserts many plops in a single transaction (one commit), for
|
||||
// bulk fills. Returns the stored rows in order. An empty input is a no-op.
|
||||
func (d *DB) CreatePlantings(ctx context.Context, plantings []*domain.Planting) ([]domain.Planting, error) {
|
||||
if len(plantings) == 0 {
|
||||
return []domain.Planting{}, nil
|
||||
}
|
||||
tx, err := d.sql.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: begin plantings tx: %w", err)
|
||||
}
|
||||
defer tx.Rollback() //nolint:errcheck // no-op after a successful commit
|
||||
|
||||
const stmt = `INSERT INTO plantings (object_id, plant_id, x_cm, y_cm, radius_cm, count, label, planted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
RETURNING ` + plantingColumns
|
||||
out := make([]domain.Planting, 0, len(plantings))
|
||||
for _, p := range plantings {
|
||||
created, err := scanPlanting(tx.QueryRowContext(ctx, stmt,
|
||||
p.ObjectID, p.PlantID, p.XCM, p.YCM, p.RadiusCM, p.Count, p.Label, p.PlantedAt))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: insert planting (batch): %w", err)
|
||||
}
|
||||
out = append(out, *created)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, fmt.Errorf("store: commit plantings: %w", err)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// UpdatePlanting applies a version-guarded update of all mutable columns (the
|
||||
// service merges partial patches first). Returns the updated row, or
|
||||
// (current row, ErrVersionConflict) / ErrNotFound — the same contract as the
|
||||
|
||||
Reference in New Issue
Block a user