package database import ( "bytes" "context" "encoding/gob" "fmt" "time" "github.com/akrylysov/pogreb" "github.com/akrylysov/pogreb/fs" "github.com/go-acme/lego/v4/certificate" "github.com/rs/zerolog/log" ) var _ CertDB = aDB{} type aDB struct { ctx context.Context cancel context.CancelFunc intern *pogreb.DB syncInterval time.Duration } func (p aDB) Close() error { p.cancel() return p.intern.Sync() } func (p aDB) Put(name string, cert *certificate.Resource) error { var resGob bytes.Buffer if err := gob.NewEncoder(&resGob).Encode(cert); err != nil { return err } return p.intern.Put([]byte(name), resGob.Bytes()) } func (p aDB) Get(name []byte) (*certificate.Resource, error) { cert := &certificate.Resource{} resBytes, err := p.intern.Get(name) if err != nil { return nil, err } if resBytes == nil { return nil, nil } if err = gob.NewDecoder(bytes.NewBuffer(resBytes)).Decode(cert); err != nil { return nil, err } return cert, nil } func (p aDB) Delete(key []byte) error { return p.intern.Delete(key) } func (p aDB) Compact() (pogreb.CompactionResult, error) { return p.intern.Compact() } func (p aDB) Items() *pogreb.ItemIterator { return p.intern.Items() } var _ CertDB = &aDB{} func (p aDB) sync() { for { err := p.intern.Sync() if err != nil { log.Err(err).Msg("Syncing cert database failed") } select { case <-p.ctx.Done(): return case <-time.After(p.syncInterval): } } } func New(path string) (CertDB, error) { if path == "" { return nil, fmt.Errorf("path not set") } db, err := pogreb.Open(path, &pogreb.Options{ BackgroundSyncInterval: 30 * time.Second, BackgroundCompactionInterval: 6 * time.Hour, FileSystem: fs.OSMMap, }) if err != nil { return nil, err } ctx, cancel := context.WithCancel(context.Background()) result := &aDB{ ctx: ctx, cancel: cancel, intern: db, syncInterval: 5 * time.Minute, } go result.sync() return result, nil }