package storage import ( "bytes" "context" "time" "encoding/binary" "git.defalsify.org/vise.git/db" ) type TimedDb struct { db.Db tdb *SubPrefixDb ttl time.Duration parentPfx uint8 parentSession []byte matchPfx map[uint8][][]byte } func NewTimedDb(db db.Db, ttl time.Duration) *TimedDb { var b [2]byte binary.BigEndian.PutUint16(b[:], SUBPREFIX_TIME) sdb := NewSubPrefixDb(db, b[:]) return &TimedDb{ Db: db, tdb: sdb, ttl: ttl, } } func(tib *TimedDb) WithMatch(pfx uint8, keyPart []byte) *TimedDb { if tib.matchPfx == nil { tib.matchPfx = make(map[uint8][][]byte) } tib.matchPfx[pfx] = append(tib.matchPfx[pfx], keyPart) return tib } func(tib *TimedDb) checkPrefix(pfx uint8, key []byte) bool { var v []byte if tib.matchPfx == nil { return true } for _, v = range(tib.matchPfx[pfx]) { l := len(v) if l > len(key) { continue } if bytes.Equal(v, key[:l]) { return true } } return false } func(tib *TimedDb) SetPrefix(pfx uint8) { tib.Db.SetPrefix(pfx) tib.parentPfx = pfx } func(tib *TimedDb) SetSession(session string) { tib.Db.SetSession(session) tib.parentSession = []byte(session) } func(tib *TimedDb) Put(ctx context.Context, key []byte, val []byte) error { t := time.Now() b, err := t.MarshalBinary() if err != nil { return err } err = tib.Db.Put(ctx, key, val) if err != nil { return err } defer func() { tib.parentPfx = 0 tib.parentSession = nil }() if tib.checkPrefix(tib.parentPfx, key) { tib.tdb.SetSession("") k := db.ToSessionKey(tib.parentPfx, []byte(tib.parentSession), key) k = append([]byte{tib.parentPfx}, k...) err = tib.tdb.Put(ctx, k, b) if err != nil { logg.ErrorCtxf(ctx, "failed to update timestamp of record", err) } } return nil } func(tib *TimedDb) Stale(ctx context.Context, pfx uint8, sessionId string, key []byte) bool { tib.tdb.SetSession("") b := db.ToSessionKey(pfx, []byte(sessionId), key) b = append([]byte{pfx}, b...) v, err := tib.tdb.Get(ctx, b) if err != nil { logg.WarnCtxf(ctx, "no time entry", "key", key, "b", b) return false } t_now := time.Now() t_then := time.Time{} err = t_then.UnmarshalBinary(v) if err != nil { return false } return t_now.After(t_then.Add(tib.ttl)) }