2015-08-20 17:27:15 +02:00
|
|
|
import binascii
|
2016-11-03 20:42:45 +01:00
|
|
|
import logging
|
|
|
|
|
2017-10-23 21:36:50 +02:00
|
|
|
from twisted.internet import defer, task
|
2019-01-07 19:28:17 +01:00
|
|
|
from lbrynet.extras.compat import f2d
|
2019-01-21 21:55:50 +01:00
|
|
|
from lbrynet import utils
|
|
|
|
from lbrynet.conf import Config
|
2016-11-03 20:42:45 +01:00
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
2015-08-20 17:27:15 +02:00
|
|
|
|
|
|
|
|
2018-07-22 00:34:59 +02:00
|
|
|
class DHTHashAnnouncer:
|
2019-01-21 21:55:50 +01:00
|
|
|
def __init__(self, conf: Config, dht_node, storage, concurrent_announcers=None):
|
|
|
|
self.conf = conf
|
2015-08-20 17:27:15 +02:00
|
|
|
self.dht_node = dht_node
|
2018-03-27 21:12:44 +02:00
|
|
|
self.storage = storage
|
|
|
|
self.clock = dht_node.clock
|
2018-02-20 19:33:59 +01:00
|
|
|
self.peer_port = dht_node.peerPort
|
2018-03-27 21:12:44 +02:00
|
|
|
self.hash_queue = []
|
2018-08-09 18:04:14 +02:00
|
|
|
if concurrent_announcers is None:
|
2019-01-21 21:55:50 +01:00
|
|
|
self.concurrent_announcers = conf.concurrent_announcers
|
2018-08-09 18:04:14 +02:00
|
|
|
else:
|
|
|
|
self.concurrent_announcers = concurrent_announcers
|
|
|
|
self._manage_lc = None
|
|
|
|
if self.concurrent_announcers:
|
|
|
|
self._manage_lc = task.LoopingCall(self.manage)
|
|
|
|
self._manage_lc.clock = self.clock
|
2019-01-21 21:55:50 +01:00
|
|
|
self.sem = defer.DeferredSemaphore(self.concurrent_announcers or conf.concurrent_announcers or 1)
|
2015-08-20 17:27:15 +02:00
|
|
|
|
2018-03-27 21:12:44 +02:00
|
|
|
def start(self):
|
2018-08-09 18:04:14 +02:00
|
|
|
if self._manage_lc:
|
2018-08-09 15:22:55 +02:00
|
|
|
self._manage_lc.start(30)
|
2015-08-20 17:27:15 +02:00
|
|
|
|
|
|
|
def stop(self):
|
2018-08-09 18:04:14 +02:00
|
|
|
if self._manage_lc and self._manage_lc.running:
|
2018-03-27 21:12:44 +02:00
|
|
|
self._manage_lc.stop()
|
2015-08-20 17:27:15 +02:00
|
|
|
|
2017-10-23 21:36:50 +02:00
|
|
|
@defer.inlineCallbacks
|
2018-03-27 21:12:44 +02:00
|
|
|
def do_store(self, blob_hash):
|
|
|
|
storing_node_ids = yield self.dht_node.announceHaveBlob(binascii.unhexlify(blob_hash))
|
|
|
|
now = self.clock.seconds()
|
|
|
|
if storing_node_ids:
|
|
|
|
result = (now, storing_node_ids)
|
2019-01-07 19:28:17 +01:00
|
|
|
yield f2d(self.storage.update_last_announced_blob(blob_hash, now))
|
2018-03-27 21:12:44 +02:00
|
|
|
log.debug("Stored %s to %i peers", blob_hash[:16], len(storing_node_ids))
|
|
|
|
else:
|
|
|
|
result = (None, [])
|
|
|
|
self.hash_queue.remove(blob_hash)
|
|
|
|
defer.returnValue(result)
|
2017-11-07 01:48:47 +01:00
|
|
|
|
2018-03-29 21:06:08 +02:00
|
|
|
def hash_queue_size(self):
|
|
|
|
return len(self.hash_queue)
|
|
|
|
|
2018-03-27 21:12:44 +02:00
|
|
|
def _show_announce_progress(self, size, start):
|
|
|
|
queue_size = len(self.hash_queue)
|
|
|
|
average_blobs_per_second = float(size - queue_size) / (self.clock.seconds() - start)
|
|
|
|
log.info("Announced %i/%i blobs, %f blobs per second", size - queue_size, size, average_blobs_per_second)
|
2015-08-20 17:27:15 +02:00
|
|
|
|
2018-02-20 19:33:59 +01:00
|
|
|
@defer.inlineCallbacks
|
2018-03-27 21:12:44 +02:00
|
|
|
def immediate_announce(self, blob_hashes):
|
2018-03-29 00:47:37 +02:00
|
|
|
self.hash_queue.extend(b for b in blob_hashes if b not in self.hash_queue)
|
2018-03-27 21:12:44 +02:00
|
|
|
log.info("Announcing %i blobs", len(self.hash_queue))
|
|
|
|
start = self.clock.seconds()
|
|
|
|
progress_lc = task.LoopingCall(self._show_announce_progress, len(self.hash_queue), start)
|
2018-05-24 18:56:39 +02:00
|
|
|
progress_lc.clock = self.clock
|
2018-03-27 21:12:44 +02:00
|
|
|
progress_lc.start(60, now=False)
|
2018-06-06 23:11:27 +02:00
|
|
|
results = yield utils.DeferredDict(
|
|
|
|
{blob_hash: self.sem.run(self.do_store, blob_hash) for blob_hash in blob_hashes}
|
|
|
|
)
|
2018-03-27 21:12:44 +02:00
|
|
|
now = self.clock.seconds()
|
|
|
|
|
|
|
|
progress_lc.stop()
|
|
|
|
|
|
|
|
announced_to = [blob_hash for blob_hash in results if results[blob_hash][0]]
|
|
|
|
if len(announced_to) != len(results):
|
|
|
|
log.debug("Failed to announce %i blobs", len(results) - len(announced_to))
|
|
|
|
if announced_to:
|
|
|
|
log.info('Took %s seconds to announce %i of %i attempted hashes (%f hashes per second)',
|
2018-03-28 03:22:53 +02:00
|
|
|
now - start, len(announced_to), len(blob_hashes),
|
2018-03-27 21:12:44 +02:00
|
|
|
int(float(len(blob_hashes)) / float(now - start)))
|
|
|
|
defer.returnValue(results)
|
2015-08-20 17:27:15 +02:00
|
|
|
|
2018-02-20 19:33:59 +01:00
|
|
|
@defer.inlineCallbacks
|
2018-03-27 21:12:44 +02:00
|
|
|
def manage(self):
|
2018-08-09 15:00:36 +02:00
|
|
|
if not self.dht_node.contacts:
|
|
|
|
log.info("Not ready to start announcing hashes")
|
|
|
|
return
|
2019-01-07 19:28:17 +01:00
|
|
|
need_reannouncement = yield f2d(self.storage.get_blobs_to_announce())
|
2018-03-27 21:12:44 +02:00
|
|
|
if need_reannouncement:
|
|
|
|
yield self.immediate_announce(need_reannouncement)
|
|
|
|
else:
|
|
|
|
log.debug("Nothing to announce")
|