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

trie -> kv store #677

Merged
merged 3 commits into from
Jan 20, 2020
Merged

trie -> kv store #677

merged 3 commits into from
Jan 20, 2020

Conversation

arnetheduck
Copy link
Member

  • simplify data storage to key-value, tries are not relevant for NBC
  • locked-down version of lmdb dependency
  • easier to build / maintain on various platforms

@tersec
Copy link
Contributor

tersec commented Jan 15, 2020

Abstractly at least, I'm in favor of this, as RocksDB is by a wide margin the bottlenecking factor in a from-scratch build -- I guess the argument for RocksDB, despite its portability, build time and/or CI cache complexity costs, etc has been performance. How is that affected here?

@arnetheduck
Copy link
Member Author

building lmdb takes <1s on my machine which I expect to be conterbalanced by the added simplicity of this code (no rlp/trie).

there are downsides to lmdb as well - memory-mapping is not available everywhere, and has limitations on 32-bit platforms. that said, I'd consider this a stop-gap solution until we implement a hot/cold store where the cold store goes into a flat append-only file - at that point, the database will be so small that whatever advanced optimizations big databases like rocksdb do won't matter.

the main feature of this branch is to simplify the storage model to KV instead of Trie - that's a better starting point for future storage work - in fact, it would make sense to rework nim-eth to use this virtual KV interface for its trie store, just like one could imagine extracting the lmdb parts into a separate library etc. that's a bridge for another day though.

@stefantalpalaru
Copy link
Contributor

stefantalpalaru commented Jan 15, 2020

You need to also check for __MINGW32__ besides __MINGW64__: https://blog.kowalczyk.info/article/j/guide-to-predefined-macros-in-c-compilers-gcc-clang-msvc-etc..html

@arnetheduck
Copy link
Member Author

arnetheduck commented Jan 16, 2020

it's only w64 failing with the compile error though, 32-bit is fine because it builds beacon_node, except for the timeout which is strange...

@stefantalpalaru
Copy link
Contributor

it's only w64 failing with the compile error though

No. The 64-bit Windows build passes.

32-bit is fine because it builds beacon_node

No, it's not and it doesn't. Remember what I said about having 2 parallel Make jobs? One hangs and the rest run to completion. The one that hangs is "beacon_node". I found out by running mingw32_make V=3 NIMFLAGS="--listCmd" on a 32-bit Windows image. The "gcc" commands are unconditionally run in paralllel, but it was clear which one is suspicious:

user@user-PC MINGW32 ~/Desktop/status/nim-beacon-chain (lmdb)
$ gcc.exe -c  -w -mno-ms-bitfields -DWIN32_LEAN_AND_MEAN -std=c99 -std=gnu99 -IC:/Users/user/Desktop/status/nim-beacon-chain/vendor/nim-nat-traversal/vendor/miniupnp/miniupnpc -DMINIUPNP_STATICLIB -IC:/Users/user/Desktop/status/nim-beacon-chain/vendor/nim-nat-traversal/vendor/libnatpmp -DNATPMP_STATICLIB -DENABLE_STRNATPMPERR -IC:/Users/user/Desktop/status/nim-beacon-chain/vendor/nim-bearssl/bearssl/csources/src -IC:/Users/user/Desktop/status/nim-beacon-chain/vendor/nim-bearssl/bearssl/csources/inc -IC:/Users/user/Desktop/status/nim-beacon-chain/vendor/nim-bearssl/bearssl/csources/tools -DBR_USE_WIN32_TIME=1 -DBR_USE_WIN32_RAND=1 -DBR_LE_UNALIGNED=1 -IC:/Users/user/Desktop/status/nim-beacon-chain/vendor/nim-secp256k1/secp256k1_wrapper -IC:/Users/user/Desktop/status/nim-beacon-chain/vendor/nim-secp256k1/secp256k1_wrapper/secp256k1 -IC:/Users/user/Desktop/status/nim-beacon-chain/vendor/nim-secp256k1/secp256k1_wrapper/secp256k1/src -DHAVE_CONFIG_H -IC:/Users/user/Desktop/status/nim-beacon-chain/vendor/nim-bearssl/bearssl/certs -g3 -Og -gdwarf-3 -O3 -fno-strict-aliasing  -IC:/Users/user/Desktop/status/nim-beacon-chain/vendor/nimbus-build-system/vendor/Nim/lib -IC:/Users/user/Desktop/status/nim-beacon-chain/beacon_chain -o nimcache/debug/beacon_node/mdb.c.o C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:60:26: error: unknown type name 'NtCreateSectionFunc'
 typedef NTSTATUS (WINAPI NtCreateSectionFunc)
                          ^~~~~~~~~~~~~~~~~~~
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:66:8: error: unknown type name 'NtCreateSectionFunc'
 static NtCreateSectionFunc *NtCreateSection;
        ^~~~~~~~~~~~~~~~~~~
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:73:26: error: unknown type name 'NtMapViewOfSectionFunc'
 typedef NTSTATUS (WINAPI NtMapViewOfSectionFunc)
                          ^~~~~~~~~~~~~~~~~~~~~~
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:80:8: error: unknown type name 'NtMapViewOfSectionFunc'
 static NtMapViewOfSectionFunc *NtMapViewOfSection;
        ^~~~~~~~~~~~~~~~~~~~~~
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:82:26: error: unknown type name 'NtCloseFunc'
 typedef NTSTATUS (WINAPI NtCloseFunc)(HANDLE h);
                          ^~~~~~~~~~~
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:84:8: error: unknown type name 'NtCloseFunc'
 static NtCloseFunc *NtClose;
        ^~~~~~~~~~~
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:4350:14: error: unknown type name 'NTSTATUS'; did you mean 'LSTATUS'?
 mdb_nt2win32(NTSTATUS st)
              ^~~~~~~~
              LSTATUS
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c: In function 'mdb_env_map':
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:4387:7: error: called object 'NtCreateSection' is not a function or function pointer
  rc = NtCreateSection(&mh, access, NULL, NULL, secprot, SEC_RESERVE, env->me_fd);
       ^~~~~~~~~~~~~~~
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:66:29: note: declared here
 static NtCreateSectionFunc *NtCreateSection;
                             ^~~~~~~~~~~~~~~
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:4394:7: error: called object 'NtMapViewOfSection' is not a function or function pointer
  rc = NtMapViewOfSection(mh, GetCurrentProcess(), &map, 0, 0, NULL, &msize, ViewUnmap, alloctype, pageprot);
       ^~~~~~~~~~~~~~~~~~
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:80:32: note: declared here
 static NtMapViewOfSectionFunc *NtMapViewOfSection;
                                ^~~~~~~~~~~~~~~~~~
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:4398:2: error: called object 'NtClose' is not a function or function pointer
  NtClose(mh);
  ^~~~~~~
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:84:21: note: declared here
 static NtCloseFunc *NtClose;
                     ^~~~~~~
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c: In function 'mdb_env_open2':
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:4752:14: error: 'NtCloseFunc' undeclared (first use in this function); did you mean 'NtClose'?
   NtClose = (NtCloseFunc *)GetProcAddress(h, "NtClose");
              ^~~~~~~~~~~
              NtClose
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:4752:14: note: each undeclared identifier is reported only once for each function it appears in
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:4752:27: error: expected expression before ')' token
   NtClose = (NtCloseFunc *)GetProcAddress(h, "NtClose");
                           ^
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:4755:25: error: 'NtMapViewOfSectionFunc' undeclared (first use in this function); did you mean 'NtMapViewOfSection'?
   NtMapViewOfSection = (NtMapViewOfSectionFunc *)GetProcAddress(h, "NtMapViewOfSection");
                         ^~~~~~~~~~~~~~~~~~~~~~
                         NtMapViewOfSection
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:4755:49: error: expected expression before ')' token
   NtMapViewOfSection = (NtMapViewOfSectionFunc *)GetProcAddress(h, "NtMapViewOfSection");
                                                 ^
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:4758:22: error: 'NtCreateSectionFunc' undeclared (first use in this function); did you mean 'NtCreateSection'?
   NtCreateSection = (NtCreateSectionFunc *)GetProcAddress(h, "NtCreateSection");
                      ^~~~~~~~~~~~~~~~~~~
                      NtCreateSection
C:/Users/user/Desktop/status/nim-beacon-chain/vendor/lmdb/libraries/liblmdb/mdb.c:4758:43: error: expected expression before ')' token
   NtCreateSection = (NtCreateSectionFunc *)GetProcAddress(h, "NtCreateSection");

Hence the solution I gave you 13 hours ago: #677 (comment)

@arnetheduck
Copy link
Member Author

well, one step forwards - one step backwards - got a bit further with the fix though it's a mystery why it doesn't stop the build then.. ? but now it crashes on some null pointer which I guess happens when loading that symbol which kind of sucks - any other tricks up your sleeve, @stefantalpalaru ?

@arnetheduck
Copy link
Member Author

You probably need to link to ntdll.lib

the symbols are loaded at runtime, so no linking needed: https://github.com/LMDB/lmdb/blob/mdb.master/libraries/liblmdb/mdb.c#L4744

@stefantalpalaru
Copy link
Contributor

it's a mystery why it doesn't stop the build then

Old Nim bug: nim-lang/Nim#8648

any other tricks up your sleeve, @stefantalpalaru ?

Just some uint64->uint changes, the usual csize->uint (until csize_t becomes available) and a very interesting stack corruption.

It turns out that when you declare a C function with just an {.importc.} and no header, Nim gives it the "nimcall" calling convention, which is usually "fastcall". What you actually want is "cdecl" (same as "stdcall"). The former convention uses registers to pass parameters, the latter doesn't, on 32-bit: https://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions

@arnetheduck
Copy link
Member Author

C function with just an {.importc.} and no header, Nim gives it the "nimcall" calling convention

upstream bug?

arnetheduck and others added 2 commits January 18, 2020 10:08
* simplify data storage to key-value, tries are not relevant for NBC
* locked-down version of lmdb dependency
* easier to build / maintain on various platforms
@tersec
Copy link
Contributor

tersec commented Jan 20, 2020

$ make eth2_network_simulation 
Building: build/process_dashboard
Building nim-beacon-chain/tests/simulation/data/beacon_node (-d:MAX_COMMITTEES_PER_SLOT=1 -d:SLOTS_PER_EPOCH=16 -d:SECONDS_PER_SLOT=6 )
Building nim-beacon-chain/tests/simulation/data/deploy_deposit_contract
INF 2020-01-20 10:35:13+01:00 Generating deposits                        tid=2214759 outputDir=nim-beacon-chain/tests/simulation/validators randomKeys=false totalValidators=192
INF 2020-01-20 10:35:14+01:00 Generating deposits                        tid=2214759 outputDir=nim-beacon-chain/tests/simulation/validators randomKeys=true totalValidators=0
Wrote nim-beacon-chain/tests/simulation/data/state_snapshot.ssz
Wrote nim-beacon-chain/tests/simulation/data/bootstrap_nodes.txt
nim-beacon-chain/beacon_chain/beacon_node.nim(1116) beacon_node
nim-beacon-chain/beacon_chain/beacon_node.nim(138) init
nim-beacon-chain/beacon_chain/kvstore_lmdb.nim(157) init
nim-beacon-chain/beacon_chain/kvstore_lmdb.nim(50) raiseLmdbError
[[reraised from:
nim-beacon-chain/beacon_chain/beacon_node.nim(1116) beacon_node
nim-beacon-chain/vendor/nim-chronos/chronos/asyncloop.nim(935) waitFor
nim-beacon-chain/vendor/nim-chronos/chronos/asyncfutures2.nim(423) read
nim-beacon-chain/vendor/nim-chronos/chronos/asyncfutures2.nim(407) internalCheckComplete
]]
Error: unhandled exception: No such file or directory
Async traceback:
  nim-beacon-chain/beacon_chain/beacon_node.nim(1116) beacon_node
  nim-beacon-chain/beacon_chain/beacon_node.nim(138)  init
  nim-beacon-chain/beacon_chain/kvstore_lmdb.nim(157) init
  nim-beacon-chain/beacon_chain/kvstore_lmdb.nim(50)  raiseLmdbError
Exception message: No such file or directory
Exception type: [LmdbError]

@stefantalpalaru
Copy link
Contributor

Fixed.

@tersec tersec merged commit 14e1e3a into devel Jan 20, 2020
@@ -0,0 +1,94 @@
# Simple Key-Value store database interface
Copy link
Contributor

Choose a reason for hiding this comment

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

TrieDatabaseRef may have been named inappropriately, but it's already a simple key-value store. The only difference is that it comes with an additional support for discardable in-memory transactions, which doesn't complicate the API at all when it's not used.

I assume the get proc signature was changed as an optimisation (to avoid copying the result bytes to a newly allocated sequence). This optimisation could have been added to TrieDatabaseRef as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

it also has logic for maintaining an empty rlp item (thus pulling in the RLP code) and does refcounting in memory, none of which are needed here - this PR is about eliminating surface area for bugs by making sure the code does what it needs to do in the simplest way possible.

get changed for that reason, but there's a semantic change as well which in theory allows for empty values to happen. I do not have enough confidence in the test suite to perform this change on nim-eth itself - that can easily be done by someone more familiar with that code if need be, but with that said nim-beacon-chain and nimbus1 have completely different storage needs - they are likely to continue using different storage engines..

Copy link
Member Author

Choose a reason for hiding this comment

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

fwiw, a practical outcome of the complexity of TrieDatabaseRef is that every implementation of it is faulty in the face of exceptions: leaks, reader/writer transaction blocking - the code raises exceptions but is not written for exception safety

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.

4 participants