chainsyncer

Blockchain syncer driver
Log | Files | Refs | LICENSE

commit 044e85fb993b0d5b396e40fd77bc3b2813cef533
parent 927913bd02c303f2232a4a08df15888db567a310
Author: lash <dev@holbrook.no>
Date:   Tue, 26 Apr 2022 07:56:04 +0000

Allow memory-only syncing

Diffstat:
MCHANGELOG | 4++++
Mchainsyncer/store/base.py | 22+++++++++++++++-------
Mchainsyncer/store/fs.py | 5+----
Achainsyncer/store/mem.py | 34++++++++++++++++++++++++++++++++++
Mchainsyncer/unittest/store.py | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msetup.cfg | 2+-
Atests/store/test_0_mem.py | 33+++++++++++++++++++++++++++++++++
Mtests/store/test_rocksdb.py | 8++++++--
8 files changed, 147 insertions(+), 30 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG @@ -1,3 +1,7 @@ +* 0.3.5 + - Allow memory-only shep if factory set to None in store constructor +* 0.3.4 + - Use explicit bool check in filter interrupt check * 0.3.3 - Include shep persistent state bootstrap sync - Add chainsyncer extras diff --git a/chainsyncer/store/base.py b/chainsyncer/store/base.py @@ -4,6 +4,7 @@ import logging # local imports from shep.persist import PersistedState +from shep import State from shep.error import StateInvalid from chainsyncer.filter import FilterState from chainsyncer.error import ( @@ -168,15 +169,22 @@ class SyncStore: self.session_path = os.path.realpath(given_path) - def setup_sync_state(self, factory, event_callback): - self.state = PersistedState(factory.add, 2, event_callback=event_callback) + def setup_sync_state(self, factory=None, event_callback=None): + if factory == None: + self.state = State(2, event_callback=event_callback) + else: + self.state = PersistedState(factory.add, 2, event_callback=event_callback) self.state.add('SYNC') self.state.add('DONE') - def setup_filter_state(self, factory, event_callback): - filter_state_backend = PersistedState(factory.add, 0, check_alias=False, event_callback=event_callback) - self.filter_state = FilterState(filter_state_backend, scan=factory.ls) + def setup_filter_state(self, factory=None, event_callback=None): + if factory == None: + filter_state_backend = State(0, check_alias=False, event_callback=event_callback) + self.filter_state = FilterState(filter_state_backend) + else: + filter_state_backend = PersistedState(factory.add, 0, check_alias=False, event_callback=event_callback) + self.filter_state = FilterState(filter_state_backend, scan=factory.ls) self.filters = [] @@ -202,7 +210,7 @@ class SyncStore: if self.first: state_bytes = sync_state_serialize(offset, 0, target) block_number_str = str(offset) - self.state.put(block_number_str, state_bytes) + self.state.put(block_number_str, contents=state_bytes) self.filter_state.put(block_number_str) o = SyncItem(offset, target, self.state, self.filter_state) self.items[offset] = o @@ -226,7 +234,7 @@ class SyncStore: self.state.move(item.state_key, self.state.DONE) state_bytes = sync_state_serialize(item.cursor, 0, -1) - self.state.put(str(item.cursor), state_bytes) + self.state.put(str(item.cursor), contents=state_bytes) def load(self, target): diff --git a/chainsyncer/store/fs.py b/chainsyncer/store/fs.py @@ -7,10 +7,7 @@ import logging from shep.store.file import SimpleFileStoreFactory # local imports -from chainsyncer.store import ( - SyncItem, - SyncStore, - ) +from chainsyncer.store import SyncStore logg = logging.getLogger(__name__) diff --git a/chainsyncer/store/mem.py b/chainsyncer/store/mem.py @@ -0,0 +1,34 @@ +# standard imports +import logging +import os + +# external imports +from shep import State + +# local imports +from chainsyncer.store import SyncStore + +logg = logging.getLogger(__name__) + + +class SyncMemStore(SyncStore): + + def __init__(self, session_id=None, state_event_callback=None, filter_state_event_callback=None): + super(SyncMemStore, self).__init__('/dev/null', session_id=session_id) + + self.session_id = os.path.basename(self.session_path) + logg.info('session id {} resolved {} path {}'.format(session_id, self.session_id, self.session_path)) + + factory = None + self.setup_sync_state(factory, state_event_callback) + + factory = None + self.setup_filter_state(factory, filter_state_event_callback) + + + def set_target(self, v): + self.target = int(v) + + + def get_target(self): + return self.target diff --git a/chainsyncer/unittest/store.py b/chainsyncer/unittest/store.py @@ -19,7 +19,17 @@ from chainsyncer.unittest import ( MockItem, ) +logging.STATETRACE = 5 logg = logging.getLogger(__name__) +logg.setLevel(logging.STATETRACE) + + +def state_change_callback(k, old_state, new_state): + logg.log(logging.STATETRACE, 'state change: {} {} -> {}'.format(k, old_state, new_state)) + + +def filter_change_callback(k, old_state, new_state): + logg.log(logging.STATETRACE, 'filter change: {} {} -> {}'.format(k, old_state, new_state)) class TestStoreBase(unittest.TestCase): @@ -27,6 +37,7 @@ class TestStoreBase(unittest.TestCase): def setUp(self): self.path = tempfile.mkdtemp() self.store_factory = None + self.persist = True @classmethod @@ -58,14 +69,28 @@ class TestStoreBase(unittest.TestCase): fp = os.path.join(self.path, store.session_id) session_id = store.session_id - st = os.stat(fp) - self.assertTrue(stat.S_ISDIR(st.st_mode)) - self.assertTrue(store.is_default) - + st = None + try: + st = os.stat(fp) + except FileNotFoundError as e: + logg.warning('error {} persist {}'.format(e, self.persist)) + if self.persist: + raise e + + if st != None: + self.assertTrue(stat.S_ISDIR(st.st_mode)) + self.assertTrue(store.is_default) + fpd = os.path.join(self.path, 'default') - st = os.stat(fpd) - self.assertTrue(stat.S_ISDIR(st.st_mode)) - self.assertTrue(store.is_default) + try: + st = os.stat(fpd) + except FileNotFoundError as e: + logg.warning('error {} persist {}'.format(e, self.persist)) + if self.persist: + raise e + if st != None: + self.assertTrue(stat.S_ISDIR(st.st_mode)) + self.assertTrue(store.is_default) fpd = os.path.realpath(fpd) self.assertEqual(fpd, fp) @@ -85,9 +110,15 @@ class TestStoreBase(unittest.TestCase): store.stop(bogus_item) store = self.store_factory('foo') fpf = os.path.join(self.path, 'foo') - st = os.stat(fpf) - self.assertTrue(stat.S_ISDIR(st.st_mode)) - self.assertFalse(store.is_default) + try: + st = os.stat(fpf) + except FileNotFoundError as e: + logg.warning('error {} persist {}'.format(e, self.persist)) + if self.persist: + raise e + if st != None: + self.assertTrue(stat.S_ISDIR(st.st_mode)) + self.assertFalse(store.is_default) def t_store_start(self): @@ -97,9 +128,11 @@ class TestStoreBase(unittest.TestCase): self.assertTrue(store.first) store.stop(bogus_item) - store = self.store_factory() - store.start() - self.assertFalse(store.first) + + if self.persist: + store = self.store_factory() + store.start() + self.assertFalse(store.first) def t_store_resume(self): @@ -226,12 +259,16 @@ class TestStoreBase(unittest.TestCase): o.next(advance_block=True) session.stop(o) - store = self.store_factory('foo') - store.start() - o = store.get(2) + if self.persist: + store = self.store_factory('foo') + store.start() + o = store.get(2) def t_sync_history_interrupted(self): + if not self.persist: + return + bogus_item = MockItem(0, 0, 0, 0) store = self.store_factory('foo') session = SyncSession(store) diff --git a/setup.cfg b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = chainsyncer -version = 0.3.3 +version = 0.3.5 description = Generic blockchain syncer driver author = Louis Holbrook author_email = dev@holbrook.no diff --git a/tests/store/test_0_mem.py b/tests/store/test_0_mem.py @@ -0,0 +1,33 @@ +# standard imports +import unittest +import logging + +# external imports +from shep import State + +# local imports +from chainsyncer.store.mem import SyncMemStore +from chainsyncer.unittest.store import TestStoreBase + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + + +class StoreFactory: + + def create(self, session_id=None): + return SyncMemStore(session_id=session_id) + + +class TestMem(TestStoreBase): + + def setUp(self): + super(TestMem, self).setUp() + self.store_factory = StoreFactory().create + self.persist = False + + +if __name__ == '__main__': + TestStoreBase.link(TestMem) + # Remove tests that test persistence of state + unittest.main() diff --git a/tests/store/test_rocksdb.py b/tests/store/test_rocksdb.py @@ -4,7 +4,11 @@ import logging # local imports from chainsyncer.store.rocksdb import SyncRocksDbStore -from chainsyncer.unittest.store import TestStoreBase +from chainsyncer.unittest.store import ( + TestStoreBase, + filter_change_callback, + state_change_callback, + ) logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger() @@ -16,7 +20,7 @@ class StoreFactory: def create(self, session_id=None): - return SyncRocksDbStore(self.path, session_id=session_id) + return SyncRocksDbStore(self.path, session_id=session_id, state_event_callback=state_change_callback, filter_state_event_callback=filter_change_callback) class TestRocksDb(TestStoreBase):