Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(internal/database): replace chaindb/badgerdb with pebbledb #3434

Merged
merged 30 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1282977
wip: introduce pebbledb, remove chaindb/badger
EclesioMeloJunior Jul 24, 2023
8ed732d
feat: replace chaindb with pebbledb
EclesioMeloJunior Jul 31, 2023
426fd88
chore: introduce pebble + remove chaindb
EclesioMeloJunior Aug 10, 2023
c0da568
chore: remove unneeded deltas
EclesioMeloJunior Aug 10, 2023
fc01e3f
chore: use `database.ErrNotFound` to wrap `pebble.ErrNotFound`
EclesioMeloJunior Aug 10, 2023
c3c6d6d
chore: add license
EclesioMeloJunior Aug 10, 2023
77fa81f
chore: remove errors wrappers over database iteractions
EclesioMeloJunior Aug 10, 2023
4d1f09d
replace `Key not found` with `pebble: not found`
EclesioMeloJunior Aug 10, 2023
0221d8b
chore: avoiding database twice
EclesioMeloJunior Aug 10, 2023
ec82fe5
chore: adjust `isNodeInitialised` to return `bool` instead of `error`
EclesioMeloJunior Aug 10, 2023
9098ffa
chore: closing db when finish tests
EclesioMeloJunior Aug 10, 2023
b041eec
chore: fix `testDelGetter` test
EclesioMeloJunior Aug 11, 2023
de692ac
chore: introduces `ClearDatabase` and add tests
EclesioMeloJunior Aug 11, 2023
31eaf13
chore: fix tests
EclesioMeloJunior Aug 11, 2023
535a25b
chore: remove `Close` method from `Table` interface
EclesioMeloJunior Aug 11, 2023
2e89be8
chore: `OfflinePruner.Prune` uses `database.Database` instead of pebb…
EclesioMeloJunior Aug 11, 2023
98258c5
chore: address comments
EclesioMeloJunior Aug 11, 2023
ec6d8a7
Merge branch 'development' into eclesio/introduce-pebbledb
EclesioMeloJunior Aug 11, 2023
8d94fbb
chore: resolve conflicts
EclesioMeloJunior Aug 11, 2023
8a4b747
chore: ignore `&pebble.Options` and move `SetupDatabase/ClearDatabase…
EclesioMeloJunior Aug 14, 2023
107a24b
chore: address README suggestion
EclesioMeloJunior Aug 14, 2023
d0717c4
chore: add license
EclesioMeloJunior Aug 14, 2023
755929b
chore: introduce `sync.WaitGroup` in babe
EclesioMeloJunior Aug 14, 2023
04f51e8
Merge branch 'development' into eclesio/introduce-pebbledb
EclesioMeloJunior Aug 14, 2023
7dd69fd
Merge branch 'development' into eclesio/introduce-pebbledb
EclesioMeloJunior Aug 15, 2023
806c9ef
Merge branch 'development' into eclesio/introduce-pebbledb
EclesioMeloJunior Aug 15, 2023
217bb7a
Merge branch 'development' into eclesio/introduce-pebbledb
EclesioMeloJunior Aug 15, 2023
85a3c59
chore: solve deepsource warns
EclesioMeloJunior Aug 15, 2023
50b4597
Merge branch 'eclesio/introduce-pebbledb' of github.com:ChainSafe/gos…
EclesioMeloJunior Aug 15, 2023
6494307
chore: address deepsource warns
EclesioMeloJunior Aug 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions cmd/gossamer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ cd gossamer
### Compile

To put the binary in ./bin, run:

```bash
make build
```
Expand Down Expand Up @@ -62,7 +63,7 @@ The node configuration can be modified in the `config.toml` file.
### Start the node

```bash
gossamer --basepath /tmp/gossamer --key alice
gossamer --basepath /tmp/gossamer --key alice
```

**Note: The `init` command is optional. If the node is not initialised, it will be initialised with the default configuration.**
Expand Down Expand Up @@ -108,19 +109,22 @@ This subcommand provides capabilities that are similar to
[Parity's Subkey utility](https://docs.substrate.io/v3/tools/subkey).

The account command supports following arguments:

- `generate` - generates a new key pair; specify `--scheme ed25519`, `--scheme secp256k1`, or `--scheme sr25519` (default)
- `list` - lists the keys in the Gossamer keystore
- `import` - imports a key from a keystore file
- `import-raw` - imports a raw key from a keystore file

Supported flags:

- `keystore-path` - path to the Gossamer keystore
- `keystore-file` - path to the keystore file
- `chain` - path to the human-readable chain-spec file
- `--scheme` - `ed25519`, `secp256k1`, or `sr25519` (default)
- `--password` - allows the user to provide a password to either encrypt a generated key or unlock the Gossamer keystore

Examples:

- `gossamer account generate --scheme ed25519` - generates an `ed25519` key pair
- `gossamer account list` - lists the keys in the Gossamer keystore
- `gossamer account import --keystore-file keystore.json` - imports a key from a keystore file
Expand All @@ -145,6 +149,7 @@ represent the Gossamer default configuration.
- `--output-path` - path to the file where the compiled chain-spec should be written

Examples:

- `gossamer build-spec --chain chain-spec.json --output-path compiled-chain-spec.json` - compiles a human-readable
chain-spec into a format that Gossamer can consume
- `gossamer build-spec --chain chain-spec.json --raw --output-path compiled-chain-spec.json` - compiles a human-readable
Expand All @@ -166,6 +171,7 @@ of a JSON file. The input for this subcommand can be retrieved from
- `--chain` - path to the human-readable chain-spec file

Examples:

- `gossamer import-state --first-slot 1 --header header.json --state state.json --chain chain-spec.json` - seeds Gossamer
storage with key-value pairs from a JSON file

Expand All @@ -185,9 +191,8 @@ What follows is a list that describes the services and capabilities that inform

#### State

This service is a wrapper around an instance of [`chaindb`](https://github.com/ChainSafe/chaindb), a key-value database
that is built on top of [BadgerDB](https://github.com/dgraph-io/badger) from [Dgraph](https://dgraph.io/). The state
service provides storage capabilities for the other Gossamer services - each service is assigned a prefix that is added
This service is a wrapper around an instance of [`pebble`](https://github.com/cockroachdb/pebble), a LevelDB/RocksDB inspired key-value database.
The state service provides storage capabilities for the other Gossamer services - each service is assigned a prefix that is added
to its storage keys. The state service is defined in [dot/state/service.go](../../dot/state/service.go).

#### Network
Expand Down Expand Up @@ -271,4 +276,4 @@ capabilities are defined in the [dot/telemetry](../../dot/telemetry) package and
The default listening address for Prometheus metrics is `localhost:9876`, and Gossamer allows the user to configure this parameter with the
`--metrics-address` command-line parameter. The Gossamer telemetry server publishes telemetry data that is compatible with
[Polkadot Telemetry](https://github.com/paritytech/substrate-telemetry) and
[its helpful UI](https://telemetry.polkadot.io/).
[its helpful UI](https://telemetry.polkadot.io/).
7 changes: 6 additions & 1 deletion cmd/gossamer/commands/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ func execInit(cmd *cobra.Command) error {
return fmt.Errorf("failed to get --force: %s", err)
}

if dot.IsNodeInitialised(config.BasePath) {
isInitialised, err := dot.IsNodeInitialised(config.BasePath)
if err != nil {
return fmt.Errorf("checking if node is initialised: %w", err)
}

if isInitialised {
// prompt user to confirm reinitialization
if force || confirmMessage("Are you sure you want to reinitialise the node? [Y/n]") {
logger.Info("reinitialising node at base path " + config.BasePath + "...")
Expand Down
7 changes: 6 additions & 1 deletion cmd/gossamer/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -601,8 +601,13 @@ func execRoot(cmd *cobra.Command) error {
return fmt.Errorf("failed to ensure root: %s", err)
}

isInitialised, err := dot.IsNodeInitialised(config.BasePath)
if err != nil {
return fmt.Errorf("failed to check is not is initialised: %w", err)
}

// if the node is not initialised, initialise it
if !dot.IsNodeInitialised(config.BasePath) {
if !isInitialised {
if err := dot.InitNode(config); err != nil {
return fmt.Errorf("failed to initialise node: %s", err)
}
Expand Down
2 changes: 1 addition & 1 deletion dot/build_spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func TestBuildFromDB(t *testing.T) {
},
}}},
{name: "invalid_db_path", path: t.TempDir(),
err: errors.New("cannot start state service: failed to create block state: cannot get block 0: Key not found")},
err: errors.New("cannot start state service: failed to create block state: cannot get block 0: pebble: not found")},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion dot/core/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/internal/database"
"github.com/ChainSafe/gossamer/internal/log"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
Expand Down Expand Up @@ -225,7 +226,7 @@ func NewTestService(t *testing.T, cfg *Config) *Service {
if stateSrvc != nil {
nodeStorage.BaseDB = stateSrvc.Base
} else {
nodeStorage.BaseDB, err = utils.SetupDatabase(filepath.Join(testDatadirPath, "offline_storage"), false)
nodeStorage.BaseDB, err = database.LoadDatabase(filepath.Join(testDatadirPath, "offline_storage"), false)
require.NoError(t, err)
}

Expand Down
7 changes: 4 additions & 3 deletions dot/mock_node_builder_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions dot/network/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,12 @@ func (s *Service) publishNetworkTelemetry(done <-chan struct{}) {

func (s *Service) sentBlockIntervalTelemetry() {
for {
select {
case <-s.ctx.Done():
return
default:
}

best, err := s.blockState.BestBlockHeader()
if err != nil {
continue
Expand Down
50 changes: 33 additions & 17 deletions dot/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/ChainSafe/gossamer/dot/system"
"github.com/ChainSafe/gossamer/dot/telemetry"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/internal/database"
"github.com/ChainSafe/gossamer/internal/log"
"github.com/ChainSafe/gossamer/internal/metrics"
"github.com/ChainSafe/gossamer/lib/babe"
Expand All @@ -34,7 +35,6 @@ import (
"github.com/ChainSafe/gossamer/lib/keystore"
"github.com/ChainSafe/gossamer/lib/runtime"
"github.com/ChainSafe/gossamer/lib/services"
"github.com/ChainSafe/gossamer/lib/utils"
)

var logger = log.NewFromGlobal(log.AddContext("pkg", "dot"))
Expand All @@ -49,7 +49,7 @@ type Node struct {
}

type nodeBuilderIface interface {
isNodeInitialised(basepath string) error
isNodeInitialised(basepath string) (bool, error)
initNode(config *cfg.Config) error
createStateService(config *cfg.Config) (*state.Service, error)
createNetworkService(config *cfg.Config, stateSrvc *state.Service, telemetryMailer Telemetry) (*network.Service,
Expand Down Expand Up @@ -78,26 +78,37 @@ type nodeBuilder struct{}

// IsNodeInitialised returns true if, within the configured data directory for the
// node, the state database has been created and the genesis data can been loaded
func IsNodeInitialised(basepath string) bool {
func IsNodeInitialised(basepath string) (bool, error) {
nodeInstance := nodeBuilder{}
err := nodeInstance.isNodeInitialised(basepath)
return err == nil
return nodeInstance.isNodeInitialised(basepath)
}

// isNodeInitialised returns nil if the node is successfully initialised
// and an error otherwise.
func (*nodeBuilder) isNodeInitialised(basepath string) error {
func (*nodeBuilder) isNodeInitialised(basepath string) (bool, error) {
// check if key registry exists
registry := filepath.Join(basepath, utils.DefaultDatabaseDir, "KEYREGISTRY")
jimjbrettj marked this conversation as resolved.
Show resolved Hide resolved
nodeDatabaseDir := filepath.Join(basepath, database.DefaultDatabaseDir)

_, err := os.Stat(registry)
if os.IsNotExist(err) {
return fmt.Errorf("cannot find key registry in database directory: %w", err)
_, err := os.Stat(nodeDatabaseDir)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}

db, err := utils.SetupDatabase(basepath, false)
entries, err := os.ReadDir(nodeDatabaseDir)
if err != nil {
return fmt.Errorf("cannot setup database: %w", err)
return false, fmt.Errorf("failed to read dir %s: %w", nodeDatabaseDir, err)
}

if len(entries) == 0 {
return false, nil
}

db, err := database.LoadDatabase(basepath, false)
if err != nil {
return false, fmt.Errorf("cannot setup database: %w", err)
}

defer func() {
Expand All @@ -109,10 +120,10 @@ func (*nodeBuilder) isNodeInitialised(basepath string) error {

_, err = state.NewBaseState(db).LoadGenesisData()
if err != nil {
return fmt.Errorf("cannot load genesis data in base state: %w", err)
return false, fmt.Errorf("cannot load genesis data in base state: %w", err)
}

return nil
return true, nil
}

// InitNode initialise the node with the given Config
Expand Down Expand Up @@ -204,7 +215,7 @@ func (*nodeBuilder) initNode(config *cfg.Config) error {
// LoadGlobalNodeName returns the stored global node name from database
func LoadGlobalNodeName(basepath string) (nodename string, err error) {
// initialise database using data directory
db, err := utils.SetupDatabase(basepath, false)
db, err := database.LoadDatabase(basepath, false)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -242,7 +253,12 @@ func newNode(config *cfg.Config,
debug.SetGCPercent(prev)
}

if builder.isNodeInitialised(config.BasePath) != nil {
isInitialised, err := builder.isNodeInitialised(config.BasePath)
if err != nil {
return nil, fmt.Errorf("checking if node is initialised: %w", err)
}

if isInitialised {
err := builder.initNode(config)
if err != nil {
return nil, fmt.Errorf("cannot initialise node: %w", err)
Expand Down Expand Up @@ -450,7 +466,7 @@ func setupTelemetry(config *cfg.Config, genesisData *genesis.Data) (mailer Telem

// stores the global node name to reuse
func storeGlobalNodeName(name, basepath string) (err error) {
db, err := utils.SetupDatabase(basepath, false)
db, err := database.LoadDatabase(basepath, false)
if err != nil {
return err
}
Expand Down
22 changes: 15 additions & 7 deletions dot/node_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
system "github.com/ChainSafe/gossamer/dot/system"
"github.com/ChainSafe/gossamer/dot/telemetry"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/internal/database"
"github.com/ChainSafe/gossamer/internal/log"
"github.com/ChainSafe/gossamer/lib/babe"
"github.com/ChainSafe/gossamer/lib/common"
Expand All @@ -35,7 +36,6 @@ import (
"github.com/ChainSafe/gossamer/lib/runtime"
wazero_runtime "github.com/ChainSafe/gossamer/lib/runtime/wazero"
"github.com/ChainSafe/gossamer/lib/trie"
"github.com/ChainSafe/gossamer/lib/utils"
gomock "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -88,7 +88,7 @@ func TestNewNode(t *testing.T) {
mockServiceRegistry.EXPECT().RegisterService(gomock.Any()).Times(8)

m := NewMocknodeBuilderIface(ctrl)
m.EXPECT().isNodeInitialised(initConfig.BasePath).Return(nil)
m.EXPECT().isNodeInitialised(initConfig.BasePath).Return(false, nil)
m.EXPECT().createStateService(initConfig).DoAndReturn(func(config *cfg.Config) (*state.Service, error) {
stateSrvc := state.NewService(stateConfig)
// create genesis from configuration file
Expand Down Expand Up @@ -214,9 +214,12 @@ func TestInitNode_Integration(t *testing.T) {
require.NoError(t, err)

// confirm database was setup
db, err := utils.SetupDatabase(config.BasePath, false)

db, err := database.LoadDatabase(config.BasePath, false)
require.NoError(t, err)
require.NotNil(t, db)
err = db.Close()
require.NoError(t, err)
}

func TestInitNode_GenesisSpec(t *testing.T) {
Expand All @@ -229,9 +232,12 @@ func TestInitNode_GenesisSpec(t *testing.T) {
err := InitNode(config)
require.NoError(t, err)
// confirm database was setup
db, err := utils.SetupDatabase(config.BasePath, false)
db, err := database.LoadDatabase(config.BasePath, false)
require.NoError(t, err)
require.NotNil(t, db)

err = db.Close()
require.NoError(t, err)
}

func TestNodeInitializedIntegration(t *testing.T) {
Expand All @@ -241,13 +247,15 @@ func TestNodeInitializedIntegration(t *testing.T) {

config.ChainSpec = genFile

result := IsNodeInitialised(config.BasePath)
result, err := IsNodeInitialised(config.BasePath)
require.NoError(t, err)
require.False(t, result)

err := InitNode(config)
err = InitNode(config)
require.NoError(t, err)

result = IsNodeInitialised(config.BasePath)
result, err = IsNodeInitialised(config.BasePath)
require.NoError(t, err)
require.True(t, result)
}

Expand Down
Loading
Loading