mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-11-04 08:21:11 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			195 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			195 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package nodb
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/binary"
 | 
						|
	"errors"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/lunny/nodb/store"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	errExpMetaKey = errors.New("invalid expire meta key")
 | 
						|
	errExpTimeKey = errors.New("invalid expire time key")
 | 
						|
)
 | 
						|
 | 
						|
type retireCallback func(*batch, []byte) int64
 | 
						|
 | 
						|
type elimination struct {
 | 
						|
	db         *DB
 | 
						|
	exp2Tx     []*batch
 | 
						|
	exp2Retire []retireCallback
 | 
						|
}
 | 
						|
 | 
						|
var errExpType = errors.New("invalid expire type")
 | 
						|
 | 
						|
func (db *DB) expEncodeTimeKey(dataType byte, key []byte, when int64) []byte {
 | 
						|
	buf := make([]byte, len(key)+11)
 | 
						|
 | 
						|
	buf[0] = db.index
 | 
						|
	buf[1] = ExpTimeType
 | 
						|
	buf[2] = dataType
 | 
						|
	pos := 3
 | 
						|
 | 
						|
	binary.BigEndian.PutUint64(buf[pos:], uint64(when))
 | 
						|
	pos += 8
 | 
						|
 | 
						|
	copy(buf[pos:], key)
 | 
						|
 | 
						|
	return buf
 | 
						|
}
 | 
						|
 | 
						|
func (db *DB) expEncodeMetaKey(dataType byte, key []byte) []byte {
 | 
						|
	buf := make([]byte, len(key)+3)
 | 
						|
 | 
						|
	buf[0] = db.index
 | 
						|
	buf[1] = ExpMetaType
 | 
						|
	buf[2] = dataType
 | 
						|
	pos := 3
 | 
						|
 | 
						|
	copy(buf[pos:], key)
 | 
						|
 | 
						|
	return buf
 | 
						|
}
 | 
						|
 | 
						|
func (db *DB) expDecodeMetaKey(mk []byte) (byte, []byte, error) {
 | 
						|
	if len(mk) <= 3 || mk[0] != db.index || mk[1] != ExpMetaType {
 | 
						|
		return 0, nil, errExpMetaKey
 | 
						|
	}
 | 
						|
 | 
						|
	return mk[2], mk[3:], nil
 | 
						|
}
 | 
						|
 | 
						|
func (db *DB) expDecodeTimeKey(tk []byte) (byte, []byte, int64, error) {
 | 
						|
	if len(tk) < 11 || tk[0] != db.index || tk[1] != ExpTimeType {
 | 
						|
		return 0, nil, 0, errExpTimeKey
 | 
						|
	}
 | 
						|
 | 
						|
	return tk[2], tk[11:], int64(binary.BigEndian.Uint64(tk[3:])), nil
 | 
						|
}
 | 
						|
 | 
						|
func (db *DB) expire(t *batch, dataType byte, key []byte, duration int64) {
 | 
						|
	db.expireAt(t, dataType, key, time.Now().Unix()+duration)
 | 
						|
}
 | 
						|
 | 
						|
func (db *DB) expireAt(t *batch, dataType byte, key []byte, when int64) {
 | 
						|
	mk := db.expEncodeMetaKey(dataType, key)
 | 
						|
	tk := db.expEncodeTimeKey(dataType, key, when)
 | 
						|
 | 
						|
	t.Put(tk, mk)
 | 
						|
	t.Put(mk, PutInt64(when))
 | 
						|
}
 | 
						|
 | 
						|
func (db *DB) ttl(dataType byte, key []byte) (t int64, err error) {
 | 
						|
	mk := db.expEncodeMetaKey(dataType, key)
 | 
						|
 | 
						|
	if t, err = Int64(db.bucket.Get(mk)); err != nil || t == 0 {
 | 
						|
		t = -1
 | 
						|
	} else {
 | 
						|
		t -= time.Now().Unix()
 | 
						|
		if t <= 0 {
 | 
						|
			t = -1
 | 
						|
		}
 | 
						|
		// if t == -1 : to remove ????
 | 
						|
	}
 | 
						|
 | 
						|
	return t, err
 | 
						|
}
 | 
						|
 | 
						|
func (db *DB) rmExpire(t *batch, dataType byte, key []byte) (int64, error) {
 | 
						|
	mk := db.expEncodeMetaKey(dataType, key)
 | 
						|
	if v, err := db.bucket.Get(mk); err != nil {
 | 
						|
		return 0, err
 | 
						|
	} else if v == nil {
 | 
						|
		return 0, nil
 | 
						|
	} else if when, err2 := Int64(v, nil); err2 != nil {
 | 
						|
		return 0, err2
 | 
						|
	} else {
 | 
						|
		tk := db.expEncodeTimeKey(dataType, key, when)
 | 
						|
		t.Delete(mk)
 | 
						|
		t.Delete(tk)
 | 
						|
		return 1, nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (db *DB) expFlush(t *batch, dataType byte) (err error) {
 | 
						|
	minKey := make([]byte, 3)
 | 
						|
	minKey[0] = db.index
 | 
						|
	minKey[1] = ExpTimeType
 | 
						|
	minKey[2] = dataType
 | 
						|
 | 
						|
	maxKey := make([]byte, 3)
 | 
						|
	maxKey[0] = db.index
 | 
						|
	maxKey[1] = ExpMetaType
 | 
						|
	maxKey[2] = dataType + 1
 | 
						|
 | 
						|
	_, err = db.flushRegion(t, minKey, maxKey)
 | 
						|
	err = t.Commit()
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
//////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
//////////////////////////////////////////////////////////
 | 
						|
 | 
						|
func newEliminator(db *DB) *elimination {
 | 
						|
	eli := new(elimination)
 | 
						|
	eli.db = db
 | 
						|
	eli.exp2Tx = make([]*batch, maxDataType)
 | 
						|
	eli.exp2Retire = make([]retireCallback, maxDataType)
 | 
						|
	return eli
 | 
						|
}
 | 
						|
 | 
						|
func (eli *elimination) regRetireContext(dataType byte, t *batch, onRetire retireCallback) {
 | 
						|
 | 
						|
	//	todo .. need to ensure exist - mapExpMetaType[expType]
 | 
						|
 | 
						|
	eli.exp2Tx[dataType] = t
 | 
						|
	eli.exp2Retire[dataType] = onRetire
 | 
						|
}
 | 
						|
 | 
						|
//	call by outside ... (from *db to another *db)
 | 
						|
func (eli *elimination) active() {
 | 
						|
	now := time.Now().Unix()
 | 
						|
	db := eli.db
 | 
						|
	dbGet := db.bucket.Get
 | 
						|
 | 
						|
	minKey := db.expEncodeTimeKey(NoneType, nil, 0)
 | 
						|
	maxKey := db.expEncodeTimeKey(maxDataType, nil, now)
 | 
						|
 | 
						|
	it := db.bucket.RangeLimitIterator(minKey, maxKey, store.RangeROpen, 0, -1)
 | 
						|
	for ; it.Valid(); it.Next() {
 | 
						|
		tk := it.RawKey()
 | 
						|
		mk := it.RawValue()
 | 
						|
 | 
						|
		dt, k, _, err := db.expDecodeTimeKey(tk)
 | 
						|
		if err != nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		t := eli.exp2Tx[dt]
 | 
						|
		onRetire := eli.exp2Retire[dt]
 | 
						|
		if tk == nil || onRetire == nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		t.Lock()
 | 
						|
 | 
						|
		if exp, err := Int64(dbGet(mk)); err == nil {
 | 
						|
			// check expire again
 | 
						|
			if exp <= now {
 | 
						|
				onRetire(t, k)
 | 
						|
				t.Delete(tk)
 | 
						|
				t.Delete(mk)
 | 
						|
 | 
						|
				t.Commit()
 | 
						|
			}
 | 
						|
 | 
						|
		}
 | 
						|
 | 
						|
		t.Unlock()
 | 
						|
	}
 | 
						|
	it.Close()
 | 
						|
 | 
						|
	return
 | 
						|
}
 |