658
658
# _copy_inventory_texts
659
659
self._text_filter = None
661
def _pack_map_and_index_list(self, index_attribute):
662
"""Convert a list of packs to an index pack map and index list.
664
:param index_attribute: The attribute that the desired index is found
666
:return: A tuple (map, list) where map contains the dict from
667
index:pack_tuple, and list contains the indices in the preferred
672
for pack_obj in self.packs:
673
index = getattr(pack_obj, index_attribute)
674
indices.append(index)
675
pack_map[index] = pack_obj
676
return pack_map, indices
678
def _index_contents(self, indices, key_filter=None):
679
"""Get an iterable of the index contents from a pack_map.
681
:param indices: The list of indices to query
682
:param key_filter: An optional filter to limit the keys returned.
684
all_index = CombinedGraphIndex(indices)
685
if key_filter is None:
686
return all_index.iter_all_entries()
688
return all_index.iter_entries(key_filter)
690
661
def pack(self, pb=None):
691
662
"""Create a new pack by reading data from other packs.
740
711
new_pack.signature_index.set_optimize(combine_backing_indices=False)
743
def _update_pack_order(self, entries, index_to_pack_map):
744
"""Determine how we want our packs to be ordered.
746
This changes the sort order of the self.packs list so that packs unused
747
by 'entries' will be at the end of the list, so that future requests
748
can avoid probing them. Used packs will be at the front of the
749
self.packs list, in the order of their first use in 'entries'.
751
:param entries: A list of (index, ...) tuples
752
:param index_to_pack_map: A mapping from index objects to pack objects.
756
for entry in entries:
758
if index not in seen_indexes:
759
packs.append(index_to_pack_map[index])
760
seen_indexes.add(index)
761
if len(packs) == len(self.packs):
762
if 'pack' in debug.debug_flags:
763
mutter('Not changing pack list, all packs used.')
765
seen_packs = set(packs)
766
for pack in self.packs:
767
if pack not in seen_packs:
770
if 'pack' in debug.debug_flags:
771
old_names = [p.access_tuple()[1] for p in self.packs]
772
new_names = [p.access_tuple()[1] for p in packs]
773
mutter('Reordering packs\nfrom: %s\n to: %s',
774
old_names, new_names)
777
714
def _copy_revision_texts(self):
778
715
"""Copy revision data to the new pack."""
779
716
raise NotImplementedError(self._copy_revision_texts)
793
730
def _create_pack_from_packs(self):
794
731
raise NotImplementedError(self._create_pack_from_packs)
796
def _least_readv_node_readv(self, nodes):
797
"""Generate request groups for nodes using the least readv's.
799
:param nodes: An iterable of graph index nodes.
800
:return: Total node count and an iterator of the data needed to perform
801
readvs to obtain the data for nodes. Each item yielded by the
802
iterator is a tuple with:
803
index, readv_vector, node_vector. readv_vector is a list ready to
804
hand to the transport readv method, and node_vector is a list of
805
(key, eol_flag, references) for the node retrieved by the
806
matching readv_vector.
808
# group by pack so we do one readv per pack
809
nodes = sorted(nodes)
812
for index, key, value, references in nodes:
813
if index not in request_groups:
814
request_groups[index] = []
815
request_groups[index].append((key, value, references))
817
for index, items in request_groups.iteritems():
818
pack_readv_requests = []
819
for key, value, references in items:
820
# ---- KnitGraphIndex.get_position
821
bits = value[1:].split(' ')
822
offset, length = int(bits[0]), int(bits[1])
823
pack_readv_requests.append(
824
((offset, length), (key, value[0], references)))
825
# linear scan up the pack to maximum range combining.
826
pack_readv_requests.sort()
827
# split out the readv and the node data.
828
pack_readv = [readv for readv, node in pack_readv_requests]
829
node_vector = [node for readv, node in pack_readv_requests]
830
result.append((index, pack_readv, node_vector))
833
733
def _log_copied_texts(self):
834
734
if 'pack' in debug.debug_flags:
835
735
mutter('%s: create_pack: file texts copied: %s%s %d items t+%6.3fs',
838
738
self.new_pack.text_index.key_count(),
839
739
time.time() - self.new_pack.start_time)
841
def _revision_node_readv(self, revision_nodes):
842
"""Return the total revisions and the readv's to issue.
844
:param revision_nodes: The revision index contents for the packs being
845
incorporated into the new pack.
846
:return: As per _least_readv_node_readv.
848
return self._least_readv_node_readv(revision_nodes)
850
741
def _use_pack(self, new_pack):
851
742
"""Return True if new_pack should be used.
856
747
return new_pack.data_inserted()
859
class OptimisingPacker(Packer):
860
"""A packer which spends more time to create better disk layouts."""
862
def _revision_node_readv(self, revision_nodes):
863
"""Return the total revisions and the readv's to issue.
865
This sort places revisions in topological order with the ancestors
868
:param revision_nodes: The revision index contents for the packs being
869
incorporated into the new pack.
870
:return: As per _least_readv_node_readv.
872
# build an ancestors dict
875
for index, key, value, references in revision_nodes:
876
ancestors[key] = references[0]
877
by_key[key] = (index, value, references)
878
order = tsort.topo_sort(ancestors)
880
# Single IO is pathological, but it will work as a starting point.
882
for key in reversed(order):
883
index, value, references = by_key[key]
884
# ---- KnitGraphIndex.get_position
885
bits = value[1:].split(' ')
886
offset, length = int(bits[0]), int(bits[1])
888
(index, [(offset, length)], [(key, value[0], references)]))
889
# TODO: combine requests in the same index that are in ascending order.
890
return total, requests
893
"""Open a pack for the pack we are creating."""
894
new_pack = super(OptimisingPacker, self).open_pack()
895
# Turn on the optimization flags for all the index builders.
896
new_pack.revision_index.set_optimize(for_size=True)
897
new_pack.inventory_index.set_optimize(for_size=True)
898
new_pack.text_index.set_optimize(for_size=True)
899
new_pack.signature_index.set_optimize(for_size=True)
903
750
class RepositoryPackCollection(object):
904
751
"""Management of packs within a repository.
906
753
:ivar _names: map of {pack_name: (index_size,)}
909
pack_factory = NewPack
910
resumed_pack_factory = ResumedPack
757
resumed_pack_factory = None
758
normal_packer_class = None
759
optimising_packer_class = None
912
761
def __init__(self, repo, transport, index_transport, upload_transport,
913
762
pack_transport, index_builder_class, index_class,
1054
903
'containing %d revisions. Packing %d files into %d affecting %d'
1055
904
' revisions', self, total_packs, total_revisions, num_old_packs,
1056
905
num_new_packs, num_revs_affected)
1057
result = self._execute_pack_operations(pack_operations,
906
result = self._execute_pack_operations(pack_operations, packer_class=self.normal_packer_class,
1058
907
reload_func=self._restart_autopack)
1059
908
mutter('Auto-packing repository %s completed', self)
1062
def _execute_pack_operations(self, pack_operations, _packer_class=Packer,
911
def _execute_pack_operations(self, pack_operations, packer_class, reload_func=None):
1064
912
"""Execute a series of pack operations.
1066
914
:param pack_operations: A list of [revision_count, packs_to_combine].
1067
:param _packer_class: The class of packer to use (default: Packer).
915
:param _packer_class: The class of packer to use
1068
916
:return: The new pack names.
1070
918
for revision_count, packs in pack_operations:
1071
919
# we may have no-ops from the setup logic
1072
920
if len(packs) == 0:
1074
packer = _packer_class(self, packs, '.autopack',
922
packer = packer_class(self, packs, '.autopack',
1075
923
reload_func=reload_func)
1144
992
# or this pack was included in the hint.
1145
993
pack_operations[-1][0] += pack.get_revision_count()
1146
994
pack_operations[-1][1].append(pack)
1147
self._execute_pack_operations(pack_operations, OptimisingPacker,
995
self._execute_pack_operations(pack_operations,
996
packer_class=self.optimising_packer_class,
1148
997
reload_func=self._restart_pack_operations)
1150
999
def plan_autopack_combinations(self, existing_packs, pack_distribution):