diff --git a/sqlchain/blockindex.go b/sqlchain/blockindex.go index a5fd16dad..96d773bef 100644 --- a/sqlchain/blockindex.go +++ b/sqlchain/blockindex.go @@ -26,7 +26,7 @@ import ( type blockNode struct { parent *blockNode - block *types.Block // TODO(leventeliu): cleanup history blocks to release memory. + block *types.Block hash hash.Hash height int32 // height is the chain height of the head count int32 // count counts the blocks (except genesis) at this head diff --git a/sqlchain/chain.go b/sqlchain/chain.go index 79e68e936..f2d79a851 100644 --- a/sqlchain/chain.go +++ b/sqlchain/chain.go @@ -44,6 +44,10 @@ import ( "github.com/syndtr/goleveldb/leveldb/util" ) +const ( + minBlockCacheTTL = int32(100) +) + var ( metaState = [4]byte{'S', 'T', 'A', 'T'} metaBlockIndex = [4]byte{'B', 'L', 'C', 'K'} @@ -359,6 +363,7 @@ func LoadChain(c *Config) (chain *Chain, err error) { st.node = last chain.rt.setHead(st) chain.st.InitTx(id) + chain.pruneBlockCache() // Read queries and rebuild memory index respIter := chain.tdb.NewIterator(util.BytesPrefix(metaResponseIndex[:]), nil) @@ -695,6 +700,7 @@ func (c *Chain) syncHead() { // runCurrentTurn does the check and runs block producing if its my turn. func (c *Chain) runCurrentTurn(now time.Time) { defer func() { + c.pruneBlockCache() c.rt.setNextTurn() c.qi.advanceBarrier(c.rt.getMinValidHeight()) c.ai.advance(c.rt.getMinValidHeight()) @@ -1187,6 +1193,11 @@ func (c *Chain) getBilling(low, high int32) (req *pt.BillingRequest, err error) } for ; n != nil && n.height >= low; n = n.parent { + // TODO(leventeliu): block maybe released, use persistence version in this case. + if n.block == nil { + continue + } + if lowBlock == nil { lowBlock = n.block } @@ -1521,3 +1532,21 @@ func (c *Chain) register(ack *types.SignedAckHeader) (err error) { func (c *Chain) remove(ack *types.SignedAckHeader) (err error) { return c.ai.remove(c.rt.getHeightFromTime(ack.SignedRequestHeader().Timestamp), ack) } + +func (c *Chain) pruneBlockCache() { + var ( + head = c.rt.getHead().node + lastCnt int32 + ) + if head == nil { + return + } + lastCnt = head.count - c.rt.blockCacheTTL + // Move to last count position + for ; head != nil && head.count > lastCnt; head = head.parent { + } + // Prune block references + for ; head != nil && head.block != nil; head = head.parent { + head.block = nil + } +} diff --git a/sqlchain/config.go b/sqlchain/config.go index 9fad34d91..113e99718 100644 --- a/sqlchain/config.go +++ b/sqlchain/config.go @@ -46,6 +46,8 @@ type Config struct { // QueryTTL sets the unacknowledged query TTL in block periods. QueryTTL int32 + BlockCacheTTL int32 + // DBAccount info TokenType pt.TokenType GasPrice uint64 diff --git a/sqlchain/runtime.go b/sqlchain/runtime.go index 1537452dc..b1935558d 100644 --- a/sqlchain/runtime.go +++ b/sqlchain/runtime.go @@ -47,6 +47,8 @@ type runtime struct { tick time.Duration // queryTTL sets the unacknowledged query TTL in block periods. queryTTL int32 + // blockCacheTTL sets the cached block numbers. + blockCacheTTL int32 // muxServer is the multiplexing service of sql-chain PRC. muxService *MuxService // price sets query price in gases. @@ -85,11 +87,17 @@ type runtime struct { // newRunTime returns a new sql-chain runtime instance with the specified config. func newRunTime(c *Config) (r *runtime) { r = &runtime{ - stopCh: make(chan struct{}), - databaseID: c.DatabaseID, - period: c.Period, - tick: c.Tick, - queryTTL: c.QueryTTL, + stopCh: make(chan struct{}), + databaseID: c.DatabaseID, + period: c.Period, + tick: c.Tick, + queryTTL: c.QueryTTL, + blockCacheTTL: func() int32 { + if c.BlockCacheTTL < minBlockCacheTTL { + return minBlockCacheTTL + } + return c.BlockCacheTTL + }(), muxService: c.MuxService, price: c.Price, producingReward: c.ProducingReward,