2018-05-17 17:24:00 +00:00
|
|
|
package mailserver
|
|
|
|
|
|
|
|
import (
|
2019-01-23 13:32:45 +00:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2018-05-17 17:24:00 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common"
|
2019-01-23 13:32:45 +00:00
|
|
|
"github.com/ethereum/go-ethereum/log"
|
2018-05-17 17:24:00 +00:00
|
|
|
"github.com/syndtr/goleveldb/leveldb"
|
|
|
|
"github.com/syndtr/goleveldb/leveldb/iterator"
|
|
|
|
"github.com/syndtr/goleveldb/leveldb/util"
|
|
|
|
)
|
|
|
|
|
2019-01-23 13:32:45 +00:00
|
|
|
const (
|
|
|
|
dbCleanerBatchSize = 1000
|
|
|
|
dbCleanerPeriod = time.Hour
|
|
|
|
)
|
|
|
|
|
|
|
|
// dbCleaner removes old messages from a db.
|
|
|
|
type dbCleaner struct {
|
|
|
|
sync.RWMutex
|
2018-05-17 17:24:00 +00:00
|
|
|
|
2018-06-27 12:22:09 +00:00
|
|
|
db dbImpl
|
2018-05-17 17:24:00 +00:00
|
|
|
batchSize int
|
2019-01-23 13:32:45 +00:00
|
|
|
retention time.Duration
|
|
|
|
|
|
|
|
period time.Duration
|
|
|
|
cancel chan struct{}
|
2018-05-17 17:24:00 +00:00
|
|
|
}
|
|
|
|
|
2019-01-23 13:32:45 +00:00
|
|
|
// newDBCleaner returns a new cleaner for db.
|
|
|
|
func newDBCleaner(db dbImpl, retention time.Duration) *dbCleaner {
|
|
|
|
return &dbCleaner{
|
2018-05-17 17:24:00 +00:00
|
|
|
db: db,
|
2019-01-23 13:32:45 +00:00
|
|
|
retention: retention,
|
|
|
|
|
|
|
|
batchSize: dbCleanerBatchSize,
|
|
|
|
period: dbCleanerPeriod,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start starts a loop that cleans up old messages.
|
|
|
|
func (c *dbCleaner) Start() {
|
|
|
|
log.Info("Starting cleaning envelopes", "period", c.period, "retention", c.retention)
|
|
|
|
|
|
|
|
cancel := make(chan struct{})
|
|
|
|
|
|
|
|
c.Lock()
|
|
|
|
c.cancel = cancel
|
|
|
|
c.Unlock()
|
|
|
|
|
|
|
|
go c.schedule(c.period, cancel)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stops stops the cleaning loop.
|
|
|
|
func (c *dbCleaner) Stop() {
|
|
|
|
c.Lock()
|
|
|
|
defer c.Unlock()
|
|
|
|
|
|
|
|
if c.cancel == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
close(c.cancel)
|
|
|
|
c.cancel = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *dbCleaner) schedule(period time.Duration, cancel <-chan struct{}) {
|
|
|
|
t := time.NewTicker(period)
|
|
|
|
defer t.Stop()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-t.C:
|
|
|
|
count, err := c.PruneEntriesOlderThan(time.Now().Add(-c.retention))
|
|
|
|
if err != nil {
|
|
|
|
log.Error("failed to prune data", "err", err)
|
|
|
|
}
|
|
|
|
log.Info("Prunned some some messages successfully", "count", count)
|
|
|
|
case <-cancel:
|
|
|
|
return
|
|
|
|
}
|
2018-05-17 17:24:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-23 13:32:45 +00:00
|
|
|
// PruneEntriesOlderThan removes messages sent between lower and upper timestamps
|
|
|
|
// and returns how many have been removed.
|
|
|
|
func (c *dbCleaner) PruneEntriesOlderThan(t time.Time) (int, error) {
|
2018-05-17 17:24:00 +00:00
|
|
|
var zero common.Hash
|
2019-01-23 13:32:45 +00:00
|
|
|
kl := NewDBKey(0, zero)
|
|
|
|
ku := NewDBKey(uint32(t.Unix()), zero)
|
2018-12-11 10:23:47 +00:00
|
|
|
i := c.db.NewIterator(&util.Range{Start: kl.Bytes(), Limit: ku.Bytes()}, nil)
|
2018-05-17 17:24:00 +00:00
|
|
|
defer i.Release()
|
|
|
|
|
|
|
|
return c.prune(i)
|
|
|
|
}
|
|
|
|
|
2019-01-23 13:32:45 +00:00
|
|
|
func (c *dbCleaner) prune(i iterator.Iterator) (int, error) {
|
2018-05-17 17:24:00 +00:00
|
|
|
batch := leveldb.Batch{}
|
|
|
|
removed := 0
|
|
|
|
|
|
|
|
for i.Next() {
|
|
|
|
batch.Delete(i.Key())
|
|
|
|
|
|
|
|
if batch.Len() == c.batchSize {
|
|
|
|
if err := c.db.Write(&batch, nil); err != nil {
|
|
|
|
return removed, err
|
|
|
|
}
|
|
|
|
|
|
|
|
removed = removed + batch.Len()
|
|
|
|
batch.Reset()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if batch.Len() > 0 {
|
|
|
|
if err := c.db.Write(&batch, nil); err != nil {
|
|
|
|
return removed, err
|
|
|
|
}
|
|
|
|
|
|
|
|
removed = removed + batch.Len()
|
|
|
|
}
|
|
|
|
|
|
|
|
return removed, nil
|
|
|
|
}
|