mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2025-03-30 13:55:27 +00:00
Merge pull request #485 from nspcc-dev/close-blockchain
core: add Close() to blockchainer, implement it to properly close chain
This commit is contained in:
commit
4d770e3c37
8 changed files with 50 additions and 23 deletions
|
@ -154,14 +154,11 @@ func dumpDB(ctx *cli.Context) error {
|
||||||
defer outStream.Close()
|
defer outStream.Close()
|
||||||
writer := io.NewBinWriterFromIO(outStream)
|
writer := io.NewBinWriterFromIO(outStream)
|
||||||
|
|
||||||
grace, cancel := context.WithCancel(newGraceContext())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
chain, err := initBlockChain(cfg)
|
chain, err := initBlockChain(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
go chain.Run(grace)
|
go chain.Run()
|
||||||
|
|
||||||
chainHeight := chain.BlockHeight()
|
chainHeight := chain.BlockHeight()
|
||||||
if skip+count > chainHeight {
|
if skip+count > chainHeight {
|
||||||
|
@ -182,6 +179,7 @@ func dumpDB(ctx *cli.Context) error {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
chain.Close()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func restoreDB(ctx *cli.Context) error {
|
func restoreDB(ctx *cli.Context) error {
|
||||||
|
@ -204,14 +202,11 @@ func restoreDB(ctx *cli.Context) error {
|
||||||
defer inStream.Close()
|
defer inStream.Close()
|
||||||
reader := io.NewBinReaderFromIO(inStream)
|
reader := io.NewBinReaderFromIO(inStream)
|
||||||
|
|
||||||
grace, cancel := context.WithCancel(newGraceContext())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
chain, err := initBlockChain(cfg)
|
chain, err := initBlockChain(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go chain.Run(grace)
|
go chain.Run()
|
||||||
|
|
||||||
var allBlocks uint32
|
var allBlocks uint32
|
||||||
reader.ReadLE(&allBlocks)
|
reader.ReadLE(&allBlocks)
|
||||||
|
@ -243,6 +238,7 @@ func restoreDB(ctx *cli.Context) error {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to add block %d: %s", i, err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to add block %d: %s", i, err), 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
chain.Close()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -272,7 +268,7 @@ func startServer(ctx *cli.Context) error {
|
||||||
errChan := make(chan error)
|
errChan := make(chan error)
|
||||||
monitoring := metrics.NewMetricsService(cfg.ApplicationConfiguration.Monitoring)
|
monitoring := metrics.NewMetricsService(cfg.ApplicationConfiguration.Monitoring)
|
||||||
|
|
||||||
go chain.Run(grace)
|
go chain.Run()
|
||||||
go server.Start(errChan)
|
go server.Start(errChan)
|
||||||
go rpcServer.Start(errChan)
|
go rpcServer.Start(errChan)
|
||||||
go monitoring.Start()
|
go monitoring.Start()
|
||||||
|
@ -295,6 +291,7 @@ Main:
|
||||||
shutdownErr = errors.Wrap(serverErr, "Error encountered whilst shutting down server")
|
shutdownErr = errors.Wrap(serverErr, "Error encountered whilst shutting down server")
|
||||||
}
|
}
|
||||||
monitoring.ShutDown()
|
monitoring.ShutDown()
|
||||||
|
chain.Close()
|
||||||
break Main
|
break Main
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -65,6 +64,10 @@ type Blockchain struct {
|
||||||
headersOp chan headersOpFunc
|
headersOp chan headersOpFunc
|
||||||
headersOpDone chan struct{}
|
headersOpDone chan struct{}
|
||||||
|
|
||||||
|
// Stop synchronization mechanisms.
|
||||||
|
stopCh chan struct{}
|
||||||
|
runToExitCh chan struct{}
|
||||||
|
|
||||||
memPool MemPool
|
memPool MemPool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +81,8 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration) (*Blockcha
|
||||||
store: storage.NewMemCachedStore(s),
|
store: storage.NewMemCachedStore(s),
|
||||||
headersOp: make(chan headersOpFunc),
|
headersOp: make(chan headersOpFunc),
|
||||||
headersOpDone: make(chan struct{}),
|
headersOpDone: make(chan struct{}),
|
||||||
|
stopCh: make(chan struct{}),
|
||||||
|
runToExitCh: make(chan struct{}),
|
||||||
memPool: NewMemPool(50000),
|
memPool: NewMemPool(50000),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +183,7 @@ func (bc *Blockchain) init() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run runs chain loop.
|
// Run runs chain loop.
|
||||||
func (bc *Blockchain) Run(ctx context.Context) {
|
func (bc *Blockchain) Run() {
|
||||||
persistTimer := time.NewTimer(persistInterval)
|
persistTimer := time.NewTimer(persistInterval)
|
||||||
defer func() {
|
defer func() {
|
||||||
persistTimer.Stop()
|
persistTimer.Stop()
|
||||||
|
@ -188,10 +193,11 @@ func (bc *Blockchain) Run(ctx context.Context) {
|
||||||
if err := bc.store.Close(); err != nil {
|
if err := bc.store.Close(); err != nil {
|
||||||
log.Warnf("failed to close db: %s", err)
|
log.Warnf("failed to close db: %s", err)
|
||||||
}
|
}
|
||||||
|
close(bc.runToExitCh)
|
||||||
}()
|
}()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-bc.stopCh:
|
||||||
return
|
return
|
||||||
case op := <-bc.headersOp:
|
case op := <-bc.headersOp:
|
||||||
op(bc.headerList)
|
op(bc.headerList)
|
||||||
|
@ -208,6 +214,13 @@ func (bc *Blockchain) Run(ctx context.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close stops Blockchain's internal loop, syncs changes to persistent storage
|
||||||
|
// and closes it. The Blockchain is no longer functional after the call to Close.
|
||||||
|
func (bc *Blockchain) Close() {
|
||||||
|
close(bc.stopCh)
|
||||||
|
<-bc.runToExitCh
|
||||||
|
}
|
||||||
|
|
||||||
// AddBlock accepts successive block for the Blockchain, verifies it and
|
// AddBlock accepts successive block for the Blockchain, verifies it and
|
||||||
// stores internally. Eventually it will be persisted to the backing storage.
|
// stores internally. Eventually it will be persisted to the backing storage.
|
||||||
func (bc *Blockchain) AddBlock(block *Block) error {
|
func (bc *Blockchain) AddBlock(block *Block) error {
|
||||||
|
|
|
@ -155,3 +155,23 @@ func TestGetTransaction(t *testing.T) {
|
||||||
assert.NoError(t, bc.persist())
|
assert.NoError(t, bc.persist())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClose(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
assert.NotNil(t, r)
|
||||||
|
}()
|
||||||
|
bc := newTestChain(t)
|
||||||
|
blocks := makeBlocks(10)
|
||||||
|
for i := 0; i < len(blocks); i++ {
|
||||||
|
require.NoError(t, bc.AddBlock(blocks[i]))
|
||||||
|
}
|
||||||
|
bc.Close()
|
||||||
|
// It's a hack, but we use internal knowledge of MemoryStore
|
||||||
|
// implementation which makes it completely unusable (up to panicing)
|
||||||
|
// after Close().
|
||||||
|
_ = bc.store.Put([]byte{0}, []byte{1})
|
||||||
|
|
||||||
|
// This should never be executed.
|
||||||
|
assert.Nil(t, t)
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ type Blockchainer interface {
|
||||||
AddHeaders(...*Header) error
|
AddHeaders(...*Header) error
|
||||||
AddBlock(*Block) error
|
AddBlock(*Block) error
|
||||||
BlockHeight() uint32
|
BlockHeight() uint32
|
||||||
|
Close()
|
||||||
HeaderHeight() uint32
|
HeaderHeight() uint32
|
||||||
GetBlock(hash util.Uint256) (*Block, error)
|
GetBlock(hash util.Uint256) (*Block, error)
|
||||||
GetContractState(hash util.Uint160) *ContractState
|
GetContractState(hash util.Uint160) *ContractState
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -41,7 +40,7 @@ func newTestChain(t *testing.T) *Blockchain {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
go chain.Run(context.Background())
|
go chain.Run()
|
||||||
zeroHash, err := chain.GetHeader(chain.GetHeaderHash(0))
|
zeroHash, err := chain.GetHeader(chain.GetHeaderHash(0))
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
newBlockPrevHash = zeroHash.Hash()
|
newBlockPrevHash = zeroHash.Hash()
|
||||||
|
|
|
@ -52,6 +52,9 @@ func (chain *testChain) AddBlock(block *core.Block) error {
|
||||||
func (chain *testChain) BlockHeight() uint32 {
|
func (chain *testChain) BlockHeight() uint32 {
|
||||||
return atomic.LoadUint32(&chain.blockheight)
|
return atomic.LoadUint32(&chain.blockheight)
|
||||||
}
|
}
|
||||||
|
func (chain *testChain) Close() {
|
||||||
|
panic("TODO")
|
||||||
|
}
|
||||||
func (chain testChain) HeaderHeight() uint32 {
|
func (chain testChain) HeaderHeight() uint32 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -139,7 +138,7 @@ type GetAccountStateResponse struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func initServerWithInMemoryChain(ctx context.Context, t *testing.T) (*core.Blockchain, http.HandlerFunc) {
|
func initServerWithInMemoryChain(t *testing.T) (*core.Blockchain, http.HandlerFunc) {
|
||||||
var nBlocks uint32
|
var nBlocks uint32
|
||||||
|
|
||||||
net := config.ModeUnitTestNet
|
net := config.ModeUnitTestNet
|
||||||
|
@ -151,7 +150,7 @@ func initServerWithInMemoryChain(ctx context.Context, t *testing.T) (*core.Block
|
||||||
chain, err := core.NewBlockchain(memoryStore, cfg.ProtocolConfiguration)
|
chain, err := core.NewBlockchain(memoryStore, cfg.ProtocolConfiguration)
|
||||||
require.NoError(t, err, "could not create chain")
|
require.NoError(t, err, "could not create chain")
|
||||||
|
|
||||||
go chain.Run(ctx)
|
go chain.Run()
|
||||||
|
|
||||||
f, err := os.Open("testdata/50testblocks.acc")
|
f, err := os.Open("testdata/50testblocks.acc")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
|
@ -2,7 +2,6 @@ package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -10,16 +9,12 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRPC(t *testing.T) {
|
func TestRPC(t *testing.T) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
chain, handler := initServerWithInMemoryChain(t)
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
chain, handler := initServerWithInMemoryChain(ctx, t)
|
|
||||||
|
|
||||||
t.Run("getbestblockhash", func(t *testing.T) {
|
t.Run("getbestblockhash", func(t *testing.T) {
|
||||||
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getbestblockhash", "params": []}`
|
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getbestblockhash", "params": []}`
|
||||||
|
|
Loading…
Add table
Reference in a new issue