Take sleep intervals as arguments
- make histogram refresh its own task - make _update_histogram take bin_size argument - synchronize the mempool refresh and hisogram calc with a lock
This commit is contained in:
parent
f20fe9d7a5
commit
f781d74ed5
@ -10,6 +10,7 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import time
|
import time
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from asyncio import Lock
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
@ -92,7 +93,7 @@ class MemPool(object):
|
|||||||
hashXs: hashX -> set of all hashes of txs touching the hashX
|
hashXs: hashX -> set of all hashes of txs touching the hashX
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, coin, api):
|
def __init__(self, coin, api, refresh_secs=5.0, log_status_secs=120.0):
|
||||||
assert isinstance(api, MemPoolAPI)
|
assert isinstance(api, MemPoolAPI)
|
||||||
self.coin = coin
|
self.coin = coin
|
||||||
self.api = api
|
self.api = api
|
||||||
@ -100,6 +101,10 @@ class MemPool(object):
|
|||||||
self.txs = {}
|
self.txs = {}
|
||||||
self.hashXs = defaultdict(set) # None can be a key
|
self.hashXs = defaultdict(set) # None can be a key
|
||||||
self.cached_compact_histogram = []
|
self.cached_compact_histogram = []
|
||||||
|
self.refresh_secs = refresh_secs
|
||||||
|
self.log_status_secs = log_status_secs
|
||||||
|
# Prevents mempool refreshes during fee histogram calculation
|
||||||
|
self.lock = Lock()
|
||||||
|
|
||||||
async def _logging(self, synchronized_event):
|
async def _logging(self, synchronized_event):
|
||||||
'''Print regular logs of mempool stats.'''
|
'''Print regular logs of mempool stats.'''
|
||||||
@ -112,10 +117,18 @@ class MemPool(object):
|
|||||||
while True:
|
while True:
|
||||||
self.logger.info(f'{len(self.txs):,d} txs '
|
self.logger.info(f'{len(self.txs):,d} txs '
|
||||||
f'touching {len(self.hashXs):,d} addresses')
|
f'touching {len(self.hashXs):,d} addresses')
|
||||||
await sleep(120)
|
await sleep(self.log_status_secs)
|
||||||
await synchronized_event.wait()
|
await synchronized_event.wait()
|
||||||
|
|
||||||
def _update_histogram(self):
|
async def _refresh_histogram(self, synchronized_event):
|
||||||
|
while True:
|
||||||
|
await synchronized_event.wait()
|
||||||
|
async with self.lock:
|
||||||
|
# Threaded as can be expensive
|
||||||
|
await run_in_thread(self._update_histogram, 100_000)
|
||||||
|
await sleep(self.coin.MEMPOOL_HISTOGRAM_REFRESH_SECS)
|
||||||
|
|
||||||
|
def _update_histogram(self, bin_size):
|
||||||
# Build a histogram by fee rate
|
# Build a histogram by fee rate
|
||||||
histogram = defaultdict(int)
|
histogram = defaultdict(int)
|
||||||
for tx in self.txs.values():
|
for tx in self.txs.values():
|
||||||
@ -132,7 +145,6 @@ class MemPool(object):
|
|||||||
compact = []
|
compact = []
|
||||||
cum_size = 0
|
cum_size = 0
|
||||||
r = 0 # ?
|
r = 0 # ?
|
||||||
bin_size = 100 * 1000
|
|
||||||
for fee_rate, size in sorted(histogram.items(), reverse=True):
|
for fee_rate, size in sorted(histogram.items(), reverse=True):
|
||||||
cum_size += size
|
cum_size += size
|
||||||
if cum_size + r > bin_size:
|
if cum_size + r > bin_size:
|
||||||
@ -187,22 +199,18 @@ class MemPool(object):
|
|||||||
|
|
||||||
async def _refresh_hashes(self, synchronized_event):
|
async def _refresh_hashes(self, synchronized_event):
|
||||||
'''Refresh our view of the daemon's mempool.'''
|
'''Refresh our view of the daemon's mempool.'''
|
||||||
secs = 5
|
while True:
|
||||||
histogram_refresh = self.coin.MEMPOOL_HISTOGRAM_REFRESH_SECS // secs
|
|
||||||
for loop_count in itertools.count():
|
|
||||||
height = self.api.cached_height()
|
height = self.api.cached_height()
|
||||||
hex_hashes = await self.api.mempool_hashes()
|
hex_hashes = await self.api.mempool_hashes()
|
||||||
if height != await self.api.height():
|
if height != await self.api.height():
|
||||||
continue
|
continue
|
||||||
hashes = set(hex_str_to_hash(hh) for hh in hex_hashes)
|
hashes = set(hex_str_to_hash(hh) for hh in hex_hashes)
|
||||||
touched = await self._process_mempool(hashes)
|
async with self.lock:
|
||||||
|
touched = await self._process_mempool(hashes)
|
||||||
synchronized_event.set()
|
synchronized_event.set()
|
||||||
synchronized_event.clear()
|
synchronized_event.clear()
|
||||||
await self.api.on_mempool(touched, height)
|
await self.api.on_mempool(touched, height)
|
||||||
# Thread mempool histogram refreshes - they can be expensive
|
await sleep(self.refresh_secs)
|
||||||
if loop_count % histogram_refresh == 0:
|
|
||||||
await run_in_thread(self._update_histogram)
|
|
||||||
await sleep(secs)
|
|
||||||
|
|
||||||
async def _process_mempool(self, all_hashes):
|
async def _process_mempool(self, all_hashes):
|
||||||
# Re-sync with the new set of hashes
|
# Re-sync with the new set of hashes
|
||||||
@ -294,6 +302,7 @@ class MemPool(object):
|
|||||||
'''Keep the mempool synchronized with the daemon.'''
|
'''Keep the mempool synchronized with the daemon.'''
|
||||||
async with TaskGroup(wait=any) as group:
|
async with TaskGroup(wait=any) as group:
|
||||||
await group.spawn(self._refresh_hashes(synchronized_event))
|
await group.spawn(self._refresh_hashes(synchronized_event))
|
||||||
|
await group.spawn(self._refresh_histogram(synchronized_event))
|
||||||
await group.spawn(self._logging(synchronized_event))
|
await group.spawn(self._logging(synchronized_event))
|
||||||
|
|
||||||
async def balance_delta(self, hashX):
|
async def balance_delta(self, hashX):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user