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

[Merged by Bors] - sql: store ballots, blocks and layers in sqlite #3047

Closed
wants to merge 46 commits into from

Conversation

dshulyak
Copy link
Contributor

@dshulyak dshulyak commented Jan 7, 2022

Motivation

The existing approach lacks atomicity, causal durability (e.g. an atx may not be on disk when a ballot is saved), and the durability can't be enforced in general without running every leveldb operation in sync mode. All this problems will result in subtle bugs that are hard to diagnose.

For those 3 requirements, we want to maintain all state in a single db. Moving state to a single leveldb will require us to enforce isolation ourselves (by maintaining separate namespace and manually concatenating keys like we do in some modules), beside that we have to create every single index manually while with sqlite we can just do CREATE INDEX and sqlite will do it for us and probably do a better job.

Another significant benefit is that we can duplicate some state in sql table to avoid loading the whole structure into memory. For instance it will be relevant for atx, which is a large (10kb) and usually, after it was validated, we want to know only the associated smesher and weight of the atx. This would be problematic with leveldb, and would require adding custom index.

related: #2918

Changes

  • general plumbing for core database stuff (db, transaction, migrations) using https://github.com/crawshaw/sqlite that is a relatively simple wrapper around C sqlite
  • tables and for layers, blocks and ballots
  • reworked ZeroLayer, it is relevant only for hare_output, which can be in 3 states - nil, empty, non-empty.
    SetZeroLayer update hare_output to empty state, at which tortoise will vote against all blocks within hdist.
  • updated to golang 1.16 for embed module. note that there is a bug with go mode tidy in 1.16 so i had to manually fix go.sum and disable go mod tidy on ci - cmd/go: missing sum after updating a different package golang/go#44129

Test Plan

existing and new uts

@dshulyak dshulyak changed the title Sqlite database sql: store ballots and blocks in sqlite database Jan 7, 2022
@dshulyak dshulyak marked this pull request as draft January 7, 2022 09:06
@countvonzero
Copy link
Contributor

wow! super happy to move to relational database for data integrity. a couple high level questions

  • do you observe any performance degradation?
    in one of my previous project we ported geth to use sqlite because we need multi-threaded read and leveldb doesn't support that. my recollection was that there was a performance penalty due to the sheer amount of data.

  • wrt to go version, why not go straight to 17? the tidy bug seems to be fixed at that version.

@dshulyak
Copy link
Contributor Author

do you observe any performance degradation?

i observed an improvement with more data, and small degradation when there is very little data.
for example for loading 10 000 layers

benchmark                             old ns/op       new ns/op       delta
BenchmarkRerun/Verifying/10000-16     15456916985     13337943478     -13.71%

but relative improvement will be much larger than -13.71% when encoding will be replaced, as xdr wastes a lot of time.
with 100, 1000 layers leveldb is slightly faster, but i think this is less relevant.

i actually expected sqlite to be faster, as leveldb data structure is write-optimized. @countvonzero do you remember if database/sql was used in the previous project or a thin wrapper? there are some explanations why database/sql is not a good choice for sqlite, as sqlite is very different from other databases (https://turriate.com/articles/making-sqlite-faster-in-go, https://crawshaw.io/blog/go-and-sqlite).

wrt to go version, why not go straight to 17? the tidy bug seems to be fixed at that version.

@noamnelke was against updating versions if we are not using new features, so i decided not to rush with 1.17. anyway i think we will want to use 1.18 in near future, as it introduces significant improvements to the language (fuzzing support in stdlib, and generics)

@countvonzero
Copy link
Contributor

do you remember if database/sql was used in the previous project or a thin wrapper?

yes. we were. we didn't explore other libraries further because core geth devs did not support the change.

Copy link
Contributor

@countvonzero countvonzero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initial pass only on sql package

if err != io.EOF {
return nil, fmt.Errorf("copy sig %w", err)
}
sigBytes = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be an error too? same for pubkeyBytes
a ballot has to have signature and public key

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i made it this way because for testing i don't need signature and pubkey, can change it to a stricter validation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, it is very inconvenient to use valid signature and pubkey for testing, so i would rather leave it as it is, and enforce validation in other places (like it works now)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm... for me, the biggest benefit of using sql-like database is the guarantee on data integrity. if we cannot trust that whatever in the database is correct, then it lost a lot of its value to me.

we should have NOT NULL constraints for these two fields for ballot.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is a benefit for sure but not the most significant. i think that it doesn't matter much because we didn't enforce it with leveldb, so for me it is irrelevant if it will be enforced with sqlite.

what i see as a main benefit is that it is much harder to make mistakes writing custom keys, when i joined almost all mesh dbcode was subtly (and some not so much subtly) broken

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but what is the cost of adding them? why not add them? isn't it nice to be able to reap more benefit from sqlite?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added for layers, but can't add them for signature and pubkey because genesis ballot doesn't have them and it messes up some tests.

sql/layers/layers.go Outdated Show resolved Hide resolved
sql/layers/layers.go Outdated Show resolved Hide resolved
sql/layers/layers.go Outdated Show resolved Hide resolved
sql/migrations.go Show resolved Hide resolved
sql/layers/layers.go Show resolved Hide resolved
sql/database.go Show resolved Hide resolved
sql/blocks/blocks.go Outdated Show resolved Hide resolved
sql/blocks/blocks.go Outdated Show resolved Hide resolved
sql/migrations/1_initial.sql Outdated Show resolved Hide resolved
.github/workflows/ci.yml Outdated Show resolved Hide resolved
.golangci.yml Show resolved Hide resolved
.github/workflows/ci.yml Outdated Show resolved Hide resolved
mesh/meshdb.go Outdated Show resolved Hide resolved
sql/ballots/ballots.go Outdated Show resolved Hide resolved
sql/ballots/ballots.go Outdated Show resolved Hide resolved
sql/ballots/ballots.go Outdated Show resolved Hide resolved
sql/ballots/ballots.go Show resolved Hide resolved
sql/blocks/blocks.go Outdated Show resolved Hide resolved
mesh/mesh.go Outdated Show resolved Hide resolved
mesh/mesh.go Outdated Show resolved Hide resolved
sql/migrations/0001_initial.sql Outdated Show resolved Hide resolved
syncer/syncer_test.go Show resolved Hide resolved
appveyor.yml Outdated Show resolved Hide resolved
hare/hare.go Show resolved Hide resolved
@dshulyak
Copy link
Contributor Author

bors merge

bors bot pushed a commit that referenced this pull request Jan 14, 2022
## Motivation

The existing approach lacks atomicity, causal durability (e.g. an atx may not be on disk when a ballot is saved), and the durability can't be enforced in general without running every leveldb operation in sync mode. All this problems will result in subtle bugs that are hard to diagnose.

For those 3 requirements, we want to maintain all state in a single db. Moving state to a single leveldb will require us to enforce isolation ourselves (by maintaining separate namespace and manually concatenating keys like we do in some modules), beside that we have to create every single index manually while with sqlite we can just do `CREATE INDEX` and sqlite will do it for us and probably do a better job. 

Another significant benefit is that we can duplicate some state in sql table to avoid loading the whole structure into memory. For instance it will be relevant for atx, which is a large (10kb) and usually, after it was validated, we want to know only the associated smesher and weight of the atx. This would be problematic with leveldb, and would require adding custom index.

related: #2918

## Changes
- general plumbing for core database stuff (db, transaction, migrations) using https://github.com/crawshaw/sqlite that is a relatively simple wrapper around C sqlite 
- tables and for layers, blocks and ballots
- reworked ZeroLayer, it is relevant only for hare_output, which can be in 3 states - nil, empty, non-empty. 
  SetZeroLayer update hare_output to empty state, at which tortoise will vote against all blocks within hdist. 
- updated to golang 1.16 for `embed` module. note that there is a bug with go mode tidy in 1.16 so i had to manually fix go.sum and disable go mod tidy on ci - golang/go#44129

## Test Plan
existing and new uts
@bors
Copy link

bors bot commented Jan 14, 2022

Build failed:

@dshulyak
Copy link
Contributor Author

bors merge

bors bot pushed a commit that referenced this pull request Jan 14, 2022
## Motivation

The existing approach lacks atomicity, causal durability (e.g. an atx may not be on disk when a ballot is saved), and the durability can't be enforced in general without running every leveldb operation in sync mode. All this problems will result in subtle bugs that are hard to diagnose.

For those 3 requirements, we want to maintain all state in a single db. Moving state to a single leveldb will require us to enforce isolation ourselves (by maintaining separate namespace and manually concatenating keys like we do in some modules), beside that we have to create every single index manually while with sqlite we can just do `CREATE INDEX` and sqlite will do it for us and probably do a better job. 

Another significant benefit is that we can duplicate some state in sql table to avoid loading the whole structure into memory. For instance it will be relevant for atx, which is a large (10kb) and usually, after it was validated, we want to know only the associated smesher and weight of the atx. This would be problematic with leveldb, and would require adding custom index.

related: #2918

## Changes
- general plumbing for core database stuff (db, transaction, migrations) using https://github.com/crawshaw/sqlite that is a relatively simple wrapper around C sqlite 
- tables and for layers, blocks and ballots
- reworked ZeroLayer, it is relevant only for hare_output, which can be in 3 states - nil, empty, non-empty. 
  SetZeroLayer update hare_output to empty state, at which tortoise will vote against all blocks within hdist. 
- updated to golang 1.16 for `embed` module. note that there is a bug with go mode tidy in 1.16 so i had to manually fix go.sum and disable go mod tidy on ci - golang/go#44129

## Test Plan
existing and new uts
@bors
Copy link

bors bot commented Jan 14, 2022

Pull request successfully merged into develop.

Build succeeded:

@bors bors bot changed the title sql: store ballots, blocks and layers in sqlite [Merged by Bors] - sql: store ballots, blocks and layers in sqlite Jan 14, 2022
@bors bors bot closed this Jan 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants