Ejemplo n.º 1
0
    def __init__(self, rref, server, verifycap, commonshare, node,
                 download_status, shnum, dyhb_rtt, logparent):
        self._rref = rref
        self._server = server
        self._node = node # holds share_hash_tree and UEB
        self.actual_segment_size = node.segment_size # might still be None
        # XXX change node.guessed_segment_size to
        # node.best_guess_segment_size(), which should give us the real ones
        # if known, else its guess.
        self._guess_offsets(verifycap, node.guessed_segment_size)
        self.actual_offsets = None
        self._UEB_length = None
        self._commonshare = commonshare # holds block_hash_tree
        self._download_status = download_status
        self._storage_index = verifycap.storage_index
        self._si_prefix = base32.b2a(verifycap.storage_index)[:8]
        self._shnum = shnum
        self._dyhb_rtt = dyhb_rtt
        # self._alive becomes False upon fatal corruption or server error
        self._alive = True
        self._loop_scheduled = False
        self._lp = log.msg(format="%(share)s created", share=repr(self),
                           level=log.NOISY, parent=logparent, umid="P7hv2w")

        self._pending = Spans() # request sent but no response received yet
        self._received = DataSpans() # ACK response received, with data
        self._unavailable = Spans() # NAK response received, no data

        # any given byte of the share can be in one of four states:
        #  in: _wanted, _requested, _received
        #      FALSE    FALSE       FALSE : don't care about it at all
        #      TRUE     FALSE       FALSE : want it, haven't yet asked for it
        #      TRUE     TRUE        FALSE : request is in-flight
        #                                   or didn't get it
        #      FALSE    TRUE        TRUE  : got it, haven't used it yet
        #      FALSE    TRUE        FALSE : got it and used it
        #      FALSE    FALSE       FALSE : block consumed, ready to ask again
        #
        # when we request data and get a NAK, we leave it in _requested
        # to remind ourself to not ask for it again. We don't explicitly
        # remove it from anything (maybe this should change).
        #
        # We retain the hashtrees in the Node, so we leave those spans in
        # _requested (and never ask for them again, as long as the Node is
        # alive). But we don't retain data blocks (too big), so when we
        # consume a data block, we remove it from _requested, so a later
        # download can re-fetch it.

        self._requested_blocks = [] # (segnum, set(observer2..))
        v = server.get_version()
        ver = v[b"http://allmydata.org/tahoe/protocols/storage/v1"]
        self._overrun_ok = ver[b"tolerates-immutable-read-overrun"]
        # If _overrun_ok and we guess the offsets correctly, we can get
        # everything in one RTT. If _overrun_ok and we guess wrong, we might
        # need two RTT (but we could get lucky and do it in one). If overrun
        # is *not* ok (tahoe-1.3.0 or earlier), we need four RTT: 1=version,
        # 2=offset table, 3=UEB_length and everything else (hashes, block),
        # 4=UEB.

        self.had_corruption = False # for unit tests
Ejemplo n.º 2
0
    def _satisfy_data_block(self, segnum, observers):
        tail = (segnum == self._node.num_segments - 1)
        datastart = self.actual_offsets["data"]
        blockstart = datastart + segnum * self._node.block_size
        blocklen = self._node.block_size
        if tail:
            blocklen = self._node.tail_block_size

        block = self._received.pop(blockstart, blocklen)
        if not block:
            log.msg("no data for block %s (want [%d:+%d])" %
                    (repr(self), blockstart, blocklen),
                    level=log.NOISY,
                    parent=self._lp,
                    umid="aK0RFw")
            return False
        log.msg(format="%(share)s._satisfy_data_block [%(start)d:+%(length)d]",
                share=repr(self),
                start=blockstart,
                length=blocklen,
                level=log.NOISY,
                parent=self._lp,
                umid="uTDNZg")
        # this block is being retired, either as COMPLETE or CORRUPT, since
        # no further data reads will help
        assert self._requested_blocks[0][0] == segnum
        try:
            self._commonshare.check_block(segnum, block)
            # hurrah, we have a valid block. Deliver it.
            for o in observers:
                # goes to SegmentFetcher._block_request_activity
                o.notify(state=COMPLETE, block=block)
            # now clear our received data, to dodge the #1170 spans.py
            # complexity bug
            self._received = DataSpans()
        except (BadHashError, NotEnoughHashesError) as e:
            # rats, we have a corrupt block. Notify our clients that they
            # need to look elsewhere, and advise the server. Unlike
            # corruption in other parts of the share, this doesn't cause us
            # to abandon the whole share.
            f = Failure(e)
            log.msg(format="hash failure in block %(segnum)d, from %(share)s",
                    segnum=segnum,
                    share=repr(self),
                    failure=f,
                    level=log.WEIRD,
                    parent=self._lp,
                    umid="mZjkqA")
            for o in observers:
                o.notify(state=CORRUPT)
            self._signal_corruption(f, blockstart, blocklen)
            self.had_corruption = True
        # in either case, we've retired this block
        self._requested_blocks.pop(0)
        # popping the request keeps us from turning around and wanting the
        # block again right away
        return True  # got satisfaction
Ejemplo n.º 3
0
    def add(self, verinfo, shnum, offset, data):
        seqnum = verinfo[0]
        if seqnum > self.seqnum:
            self._clear()
            self.seqnum = seqnum

        index = (verinfo, shnum)
        if index in self.cache:
            self.cache[index].add(offset, data)
        else:
            spans = DataSpans()
            spans.add(offset, data)
            self.cache[index] = spans
Ejemplo n.º 4
0
 def init(self, N):
     self.s = DataSpans()