go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

log.go (4234B)


      1 package log
      2 
      3 import (
      4 	"context"
      5 	"encoding/binary"
      6 	"time"
      7 
      8 	"git.defalsify.org/vise.git/logging"
      9 	"git.defalsify.org/vise.git/lang"
     10 	"git.defalsify.org/vise.git/db"
     11 )
     12 
     13 var (
     14 	logg logging.Logger = logging.NewVanilla().WithDomain("logdb")
     15 )
     16 
     17 // TODO: add a formatted dumper for the logdb
     18 type logDb struct {
     19 	db.Db
     20 	logDb db.Db
     21 }
     22 
     23 type LogEntry struct {
     24 	Key []byte
     25 	Val []byte
     26 	SessionId string
     27 	When time.Time
     28 	Pfx uint8
     29 }
     30 
     31 // NewLogDb creates a wrapper for the main Db in the first argument, which write an entry for every Put to the second database.
     32 //
     33 // All interface methods operate like normal on the main Db.
     34 //
     35 // Errors writing to the log database are ignored (but logged).
     36 // 
     37 // The Put is recorded in the second database under a chronologically sorted session key:
     38 //
     39 // `db.DATATYPE_UNKNOWN | sessionId | "_" | Big-endian uint64 representation of nanoseconds of time of put`
     40 // 
     41 // The value is stored as:
     42 //
     43 // `varint(length(key)) | key | value`
     44 func NewLogDb(mainDb db.Db, subDb db.Db) *logDb {
     45 	subDb.Base().AllowUnknownPrefix()
     46 	return &logDb{
     47 		Db: mainDb,
     48 		logDb: subDb,
     49 	}
     50 }
     51 
     52 // Start implements Db
     53 func (ldb *logDb) Start(ctx context.Context) error {
     54 	err := ldb.Db.Start(ctx)
     55 	if err != nil {
     56 		return err	
     57 	}
     58 	err = ldb.logDb.Start(ctx)
     59 	if err != nil {
     60 		logg.DebugCtxf(ctx, "logdb start fail", "ctx", ctx, "err", err)
     61 	}
     62 	return nil
     63 }
     64 
     65 // Stop implements Db
     66 func (ldb *logDb) Stop(ctx context.Context) error {
     67 	err := ldb.logDb.Stop(ctx)
     68 	if err != nil {
     69 		logg.DebugCtxf(ctx, "logdb stop fail", "ctx", ctx, "err", err)
     70 	}
     71 	return ldb.Db.Stop(ctx)
     72 }
     73 
     74 // Connect implements Db.
     75 func (ldb *logDb) Connect(ctx context.Context, connStr string) error {
     76 	err := ldb.Db.Connect(ctx, connStr)
     77 	if err != nil {
     78 		return err
     79 	}
     80 	err = ldb.logDb.Connect(ctx, connStr)
     81 	if err != nil {
     82 		ldb.Db.Close(ctx)
     83 	}
     84 	return err
     85 }
     86 
     87 // SetLanguage implements Db.
     88 func (ldb *logDb) SetLanguage(ln *lang.Language) {
     89 	ldb.Db.SetLanguage(ln)
     90 	ldb.logDb.SetLanguage(ln)
     91 }
     92 
     93 // SetSession implements Db.
     94 func (ldb *logDb) SetSession(sessionId string) {
     95 	ldb.Db.SetSession(sessionId)	
     96 	ldb.logDb.SetSession(sessionId)	
     97 }
     98 
     99 // Base implement Db.
    100 func (ldb *logDb) Base() *db.DbBase {
    101 	return ldb.Db.Base()
    102 }
    103 
    104 // create the chronological logentry key to store the put under.
    105 func (ldb *logDb) toLogDbEntry(ctx context.Context, key []byte, val []byte) ([]byte, []byte) {
    106 	var innerKey []byte
    107 	var innerValKey []byte
    108 	var innerValVal []byte
    109 	var l []byte
    110 
    111 	lk, err := ldb.Base().ToKey(ctx, key)
    112 	if err != nil {
    113 		return nil, nil
    114 	}
    115 	if lk.Translation == nil {
    116 		innerValKey = lk.Default
    117 	} else {
    118 		innerValKey = lk.Translation
    119 	}
    120 
    121 	l = make([]byte, 8)
    122 	c := binary.PutUvarint(l, uint64(len(innerValKey)))
    123 	innerValKey = append(l[:c], innerValKey...)
    124 	innerValKey = append(innerValKey, val...)
    125 
    126 	innerKey = make([]byte, 8)
    127 	t := time.Now().UnixNano()
    128 	binary.BigEndian.PutUint64(innerKey, uint64(t))
    129 	return innerKey, append(innerValKey, innerValVal...)
    130 }
    131 
    132 // ToLogDbEntry decodes a logdb entry to a structure containing the relevant metadata aswell as the original key and value pass by the client.
    133 func (ldb *logDb) ToLogDbEntry(ctx context.Context, key []byte, val []byte) (LogEntry, error) {
    134 	var err error
    135 
    136 	key = key[1:]
    137 	tb := key[len(key)-8:]
    138 	nsecs := binary.BigEndian.Uint64(tb[:8])
    139 	nsecPart := int64(nsecs % 1000000000)
    140 	secPart := int64(nsecs / 1000000000)
    141 	t := time.Unix(secPart, nsecPart)
    142 
    143 	sessionId := key[:len(key)-8]
    144 
    145 	l, c := binary.Uvarint(val)
    146 	lk := val[c:uint64(c)+l]
    147 	v := val[uint64(c)+l:]
    148 	pfx := lk[0]
    149 	k, err := ldb.Base().DecodeKey(ctx, lk)
    150 	if err != nil {
    151 		return LogEntry{}, err
    152 	}
    153 	sessionId = lk[1:len(lk)-len(k)-1]
    154 	return LogEntry{
    155 		Key: k,
    156 		Val: v,
    157 		SessionId: string(sessionId),
    158 		When: t,
    159 		Pfx: pfx,
    160 	}, nil
    161 }
    162 
    163 // Put implements Db.
    164 func (ldb *logDb) Put(ctx context.Context, key []byte, val []byte) error {
    165 	ldb.logDb.SetPrefix(db.DATATYPE_UNKNOWN)
    166 	err := ldb.Db.Put(ctx, key, val)
    167 	if err != nil {
    168 		return err
    169 	}
    170 	key, val = ldb.toLogDbEntry(ctx, key, val)
    171 	if key == nil {
    172 		logg.DebugCtxf(ctx, "logdb kv fail", "key", key, "err", err)
    173 		return nil
    174 	}
    175 	err = ldb.logDb.Put(ctx, key, val)
    176 	if err != nil {
    177 		logg.DebugCtxf(ctx, "logdb put fail", "key", key, "err", err)
    178 	}
    179 	return nil
    180 }