Files
2026-07-20 04:54:03 +00:00

88 lines
2.9 KiB
Go

//go:build linux && agentcompat
package scenario
import (
"context"
"errors"
"github.com/nezhahq/nezha/integration/agentcompat/internal/client"
processharness "github.com/nezhahq/nezha/integration/agentcompat/internal/process"
)
var ErrStressSQLiteJournalNotDrained = errors.New("stress dashboard sqlite journal is not drained")
type stressSQLiteHoldControl interface {
ArmSQLiteHold(context.Context) (client.SQLiteHoldReceipt, error)
WaitForSQLiteHold(context.Context, client.SQLiteHoldReceipt, client.SQLiteHoldState) (client.SQLiteHoldReceipt, error)
ReleaseSQLiteHold(context.Context, client.SQLiteHoldReceipt) (client.SQLiteHoldReceipt, error)
AbortSQLiteHold(context.Context, client.SQLiteHoldReceipt) (client.SQLiteHoldReceipt, error)
}
type stressSQLiteJournalWatch interface {
Wait(context.Context) error
Close() error
}
type stressSQLiteJournalWatchOpener func(string) (stressSQLiteJournalWatch, error)
func drainStressSQLiteJournal(ctx context.Context, control stressSQLiteHoldControl, writer func(context.Context) error, journalPath string, openWatch stressSQLiteJournalWatchOpener) error {
receipt, err := control.ArmSQLiteHold(ctx)
if err != nil {
return err
}
writerContext, cancelWriter := context.WithCancel(ctx)
writerDone := make(chan error, 1)
go func() { writerDone <- writer(writerContext) }()
abort := func(cause error) error {
_, abortErr := control.AbortSQLiteHold(context.WithoutCancel(ctx), receipt)
cancelWriter()
return errors.Join(cause, abortErr, <-writerDone)
}
selected, err := control.WaitForSQLiteHold(ctx, receipt, client.SQLiteHoldStateSelected)
if err != nil {
return abort(err)
}
finalizing, err := control.WaitForSQLiteHold(ctx, selected, client.SQLiteHoldStateFinalizing)
if err != nil {
return abort(err)
}
watch, err := openWatch(journalPath)
if err != nil {
return abort(err)
}
if _, err := control.ReleaseSQLiteHold(ctx, finalizing); err != nil {
return errors.Join(abort(err), watch.Close())
}
waitErr := watch.Wait(ctx)
if waitErr != nil {
cancelWriter()
}
writerErr := <-writerDone
cancelWriter()
return errors.Join(waitErr, writerErr, watch.Close())
}
func drainStressDashboardSQLiteJournal(ctx context.Context, fixture *heldSessionSetRealFixture) error {
journalPath := fixture.dashboard.DatabasePath() + "-journal"
return drainStressSQLiteJournal(ctx, fixture.controlPAT.Client, func(writerContext context.Context) error {
_, err := fixture.controlPAT.Client.IOStreamState(writerContext)
return err
}, journalPath, func(path string) (stressSQLiteJournalWatch, error) {
return processharness.OpenSQLiteJournalWatch(path)
})
}
func observeStressDashboardSQLiteJournal(path string) func(context.Context, processharness.Sample) error {
return func(_ context.Context, sample processharness.Sample) error {
held, err := processharness.ProcessHasOpenPath(sample.PID, path)
if err != nil {
return err
}
if held {
return ErrStressSQLiteJournalNotDrained
}
return nil
}
}