pprofweb/pkg/goroutineinstance/instancestore.go

85 lines
1.6 KiB
Go
Raw Normal View History

2024-10-29 03:24:09 +00:00
package goroutineinstance
import (
"encoding/json"
"fmt"
"time"
"gfx.cafe/util/go/generic"
"github.com/DataDog/gostackparse"
"github.com/rs/xid"
"github.com/spf13/afero"
)
type InstanceStore interface {
NewInstance(xs []*gostackparse.Goroutine) (*Instance, error)
GetInstance(id xid.ID) (*Instance, error)
}
type fileBasedInstanceStore struct {
expiry time.Duration
fs afero.Fs
cache generic.Map[xid.ID, *Instance]
}
func NewFileBasedInstanceStore(fs afero.Fs, expiry time.Duration) *fileBasedInstanceStore {
return &fileBasedInstanceStore{
fs: fs,
expiry: expiry,
}
}
func (o *fileBasedInstanceStore) NewInstance(dat []*gostackparse.Goroutine) (*Instance, error) {
// save instance
id := xid.New()
i := &Instance{
id: id,
dat: dat,
}
jsonDat, err := json.Marshal(dat)
if err != nil {
return nil, err
}
file, err := o.fs.Create(id.String())
if err != nil {
return nil, err
}
_, err = file.Write(jsonDat)
if err != nil {
file.Close()
return nil, err
}
err = file.Close()
if err != nil {
return nil, err
}
o.cache.Store(id, i)
return i, nil
}
func (o *fileBasedInstanceStore) GetInstance(id xid.ID) (*Instance, error) {
if i, ok := o.cache.Load(id); ok {
return i, nil
}
if o.expiry > 0 {
if id.Time().After(time.Now().Add(o.expiry)) {
o.fs.Remove(id.String())
return nil, fmt.Errorf("goroutine expired")
}
}
bts, err := afero.ReadFile(o.fs, id.String())
if err != nil {
return nil, err
}
i := &Instance{id: id}
err = json.Unmarshal(bts, &i.dat)
if err != nil {
return nil, err
}
o.cache.Store(id, i)
return i, nil
}