2780
2780
return key[:-1], key[-1]
2783
class _KeyRefs(object):
2785
def __init__(self, track_new_keys=False):
2786
# dict mapping 'key' to 'set of keys referring to that key'
2789
# set remembering all new keys
2790
self.new_keys = set()
2792
self.new_keys = None
2798
self.new_keys.clear()
2800
def add_references(self, key, refs):
2801
# Record the new references
2802
for referenced in refs:
2804
needed_by = self.refs[referenced]
2806
needed_by = self.refs[referenced] = set()
2808
# Discard references satisfied by the new key
2811
def get_new_keys(self):
2812
return self.new_keys
2814
def get_unsatisfied_refs(self):
2815
return self.refs.iterkeys()
2817
def _satisfy_refs_for_key(self, key):
2821
# No keys depended on this key. That's ok.
2824
def add_key(self, key):
2825
# satisfy refs for key, and remember that we've seen this key.
2826
self._satisfy_refs_for_key(key)
2827
if self.new_keys is not None:
2828
self.new_keys.add(key)
2830
def satisfy_refs_for_keys(self, keys):
2832
self._satisfy_refs_for_key(key)
2834
def get_referrers(self):
2836
for referrers in self.refs.itervalues():
2837
result.update(referrers)
2841
2783
class _KnitGraphIndex(object):
2842
2784
"""A KnitVersionedFiles index layered on GraphIndex."""
3275
class _DirectPackAccess(object):
3276
"""Access to data in one or more packs with less translation."""
3278
def __init__(self, index_to_packs, reload_func=None, flush_func=None):
3279
"""Create a _DirectPackAccess object.
3281
:param index_to_packs: A dict mapping index objects to the transport
3282
and file names for obtaining data.
3283
:param reload_func: A function to call if we determine that the pack
3284
files have moved and we need to reload our caches. See
3285
bzrlib.repo_fmt.pack_repo.AggregateIndex for more details.
3287
self._container_writer = None
3288
self._write_index = None
3289
self._indices = index_to_packs
3290
self._reload_func = reload_func
3291
self._flush_func = flush_func
3293
def add_raw_records(self, key_sizes, raw_data):
3294
"""Add raw knit bytes to a storage area.
3296
The data is spooled to the container writer in one bytes-record per
3299
:param sizes: An iterable of tuples containing the key and size of each
3301
:param raw_data: A bytestring containing the data.
3302
:return: A list of memos to retrieve the record later. Each memo is an
3303
opaque index memo. For _DirectPackAccess the memo is (index, pos,
3304
length), where the index field is the write_index object supplied
3305
to the PackAccess object.
3307
if type(raw_data) is not str:
3308
raise AssertionError(
3309
'data must be plain bytes was %s' % type(raw_data))
3312
for key, size in key_sizes:
3313
p_offset, p_length = self._container_writer.add_bytes_record(
3314
raw_data[offset:offset+size], [])
3316
result.append((self._write_index, p_offset, p_length))
3320
"""Flush pending writes on this access object.
3322
This will flush any buffered writes to a NewPack.
3324
if self._flush_func is not None:
3327
def get_raw_records(self, memos_for_retrieval):
3328
"""Get the raw bytes for a records.
3330
:param memos_for_retrieval: An iterable containing the (index, pos,
3331
length) memo for retrieving the bytes. The Pack access method
3332
looks up the pack to use for a given record in its index_to_pack
3334
:return: An iterator over the bytes of the records.
3336
# first pass, group into same-index requests
3338
current_index = None
3339
for (index, offset, length) in memos_for_retrieval:
3340
if current_index == index:
3341
current_list.append((offset, length))
3343
if current_index is not None:
3344
request_lists.append((current_index, current_list))
3345
current_index = index
3346
current_list = [(offset, length)]
3347
# handle the last entry
3348
if current_index is not None:
3349
request_lists.append((current_index, current_list))
3350
for index, offsets in request_lists:
3352
transport, path = self._indices[index]
3354
# A KeyError here indicates that someone has triggered an index
3355
# reload, and this index has gone missing, we need to start
3357
if self._reload_func is None:
3358
# If we don't have a _reload_func there is nothing that can
3361
raise errors.RetryWithNewPacks(index,
3362
reload_occurred=True,
3363
exc_info=sys.exc_info())
3365
reader = pack.make_readv_reader(transport, path, offsets)
3366
for names, read_func in reader.iter_records():
3367
yield read_func(None)
3368
except errors.NoSuchFile:
3369
# A NoSuchFile error indicates that a pack file has gone
3370
# missing on disk, we need to trigger a reload, and start over.
3371
if self._reload_func is None:
3373
raise errors.RetryWithNewPacks(transport.abspath(path),
3374
reload_occurred=False,
3375
exc_info=sys.exc_info())
3377
def set_writer(self, writer, index, transport_packname):
3378
"""Set a writer to use for adding data."""
3379
if index is not None:
3380
self._indices[index] = transport_packname
3381
self._container_writer = writer
3382
self._write_index = index
3384
def reload_or_raise(self, retry_exc):
3385
"""Try calling the reload function, or re-raise the original exception.
3387
This should be called after _DirectPackAccess raises a
3388
RetryWithNewPacks exception. This function will handle the common logic
3389
of determining when the error is fatal versus being temporary.
3390
It will also make sure that the original exception is raised, rather
3391
than the RetryWithNewPacks exception.
3393
If this function returns, then the calling function should retry
3394
whatever operation was being performed. Otherwise an exception will
3397
:param retry_exc: A RetryWithNewPacks exception.
3400
if self._reload_func is None:
3402
elif not self._reload_func():
3403
# The reload claimed that nothing changed
3404
if not retry_exc.reload_occurred:
3405
# If there wasn't an earlier reload, then we really were
3406
# expecting to find changes. We didn't find them, so this is a
3410
exc_class, exc_value, exc_traceback = retry_exc.exc_info
3411
raise exc_class, exc_value, exc_traceback
3414
3217
def annotate_knit(knit, revision_id):
3415
3218
"""Annotate a knit with no cached annotations.