infocalypse

(djk)
2009-05-06: Fixed fn-reinsert --level 3 so that every update appears in at least

Fixed fn-reinsert --level 3 so that every update appears in at least two re-inserted CHKs if possible.

diff --git a/infocalypse/graph.py b/infocalypse/graph.py
--- a/infocalypse/graph.py
+++ b/infocalypse/graph.py
@@ -953,33 +953,6 @@ def get_heads(graph, to_index=None):
     heads.sort()
     return tuple(heads)
 
-def get_huge_top_key_edges(graph, extant=False):
-    """ Get the list of edges in the top key edges (and
-        alternates) that are too big to salt.
-
-        If extant is True, return existing edges.
-        If extent is False, return edges that could be added. """
-    ret = []
-    edges = graph.get_top_key_edges()
-    for edge in edges:
-        if graph.get_length(edge) > MAX_METADATA_HACK_LEN:
-            if edge[2] == 1:
-                assert graph.insert_type(edge) == INSERT_HUGE
-                if extant and (not alternate in ret):
-                    ret.append(edge)
-            else:
-                assert edge[2] == 0
-                assert graph.insert_type(edge) == INSERT_NORMAL
-                alternate = (edge[0], edge[1], 1)
-                if graph.is_redundant(edge):
-                    assert graph.insert_type(alternate) == INSERT_HUGE
-                    if extant and (not alternate in ret):
-                        ret.append(alternate)
-                else:
-                    if (not extant) and (not alternate in ret):
-                        ret.append(alternate)
-
-    return ret
 # ASSUMPTIONS:
 # 0) head which don't appear in bases are tip heads. True?
 
diff --git a/infocalypse/graphutil.py b/infocalypse/graphutil.py
--- a/infocalypse/graphutil.py
+++ b/infocalypse/graphutil.py
@@ -23,7 +23,8 @@
 from binascii import hexlify
 
 from graph import FIRST_INDEX, MAX_PATH_LEN, UpdateGraph, \
-     UpdateGraphException, canonical_path_itr
+     UpdateGraphException, canonical_path_itr, edges_containing, INSERT_HUGE, \
+     INSERT_NORMAL, MAX_METADATA_HACK_LEN
 
 ############################################################
 # Doesn't dump FIRST_INDEX entry.
@@ -395,3 +396,80 @@ def minimal_graph(graph, repo, version_t
 
     return prev_minimal
 
+# REDFLAG: todo, find other places where I should be using this func.
+def find_alternate_edges(graph, edges):
+    """ Find alternate redundant edges that aren't already in edges. """
+    alternate_edges = []
+    for edge in edges:
+        if graph.is_redundant(edge):
+            alternate_edge = (edge[0], edge[1], int(not edge[2]))
+            if not alternate_edge in edges:
+                alternate_edges.append(alternate_edge)
+    return alternate_edges
+
+def find_redundant_edges(graph, current_edges, skip_huge=True):
+    """ Find the edges you would need to add to make current_edges
+        fully redundant.
+
+        Return an (edge_list, failed_indices) with the alternate edges
+        and indices which only appear in a single edge.
+        """
+    def increment_counts(counts, for_edge):
+        """ INTERNAL: Increment count for every index contained by for_edge.
+        """
+        for index in range(for_edge[0] + 1, for_edge[1] + 1):
+            counts[index] = counts[index] + 1
+
+    # Empty redundancy counts for all indexes
+    redundancy = [0, ] * (graph.latest_index + 1)
+
+    edges = set(current_edges)
+    for edge in edges:
+        increment_counts(redundancy, edge)
+
+    failed_indices = []
+    redundant_edges = []
+    for index in range(0, len(redundancy)):
+        if redundancy[index] < 2:
+            candidates = edges_containing(graph, index)
+            while len(candidates) and redundancy[index] < 2:
+                candidate = candidates.pop()
+                if candidate in edges:
+                    continue
+                if graph.insert_type(candidate) == INSERT_HUGE and skip_huge:
+                    continue
+                redundant_edges.append(candidate)
+                increment_counts(redundancy, candidate)
+            if redundancy[index] < 2:
+                failed_indices.append(index)
+
+    return (redundant_edges, failed_indices)
+
+def get_huge_top_key_edges(graph, extant=False):
+    """ Get the list of edges in the top key edges (and
+        alternates) that are too big to salt.
+
+        If extant is True, return existing edges.
+        If extent is False, return edges that could be added. """
+    ret = []
+    edges = graph.get_top_key_edges()
+    edges += find_redundant_edges(graph, edges, False)[0]
+    for edge in edges:
+        if graph.get_length(edge) > MAX_METADATA_HACK_LEN:
+            if edge[2] == 1:
+                assert graph.insert_type(edge) == INSERT_HUGE
+                if extant and (not alternate in ret):
+                    ret.append(edge)
+            else:
+                assert edge[2] == 0
+                assert graph.insert_type(edge) == INSERT_NORMAL
+                alternate = (edge[0], edge[1], 1)
+                if graph.is_redundant(edge):
+                    assert graph.insert_type(alternate) == INSERT_HUGE
+                    if extant and (not alternate in ret):
+                        ret.append(alternate)
+                else:
+                    if (not extant) and (not alternate in ret):
+                        ret.append(alternate)
+
+    return ret
diff --git a/infocalypse/insertingbundles.py b/infocalypse/insertingbundles.py
--- a/infocalypse/insertingbundles.py
+++ b/infocalypse/insertingbundles.py
@@ -22,8 +22,9 @@
 
 from graph import UpToDate, INSERT_SALTED_METADATA, INSERT_HUGE, \
      FREENET_BLOCK_LEN, build_version_table, get_heads, \
-     PENDING_INSERT1, get_huge_top_key_edges
-from graphutil import graph_to_string
+     PENDING_INSERT1
+from graphutil import graph_to_string, find_redundant_edges, \
+     find_alternate_edges, get_huge_top_key_edges
 from bundlecache import BundleException
 
 from statemachine import RequestQueueState
@@ -283,13 +284,22 @@ class InsertingBundles(RequestQueueState
                 # Only the latest update.
                 self.new_edges = self.new_edges[:1]
 
-            redundant = []
-            for edge in  self.new_edges:
-                if graph.is_redundant(edge):
-                    alternate_edge = (edge[0], edge[1], int(not edge[2]))
-                    if not alternate_edge in self.new_edges:
-                        redundant.append(alternate_edge)
-            self.new_edges += redundant
+            else:
+                pass
+
+            # Add alternate CHKs for the same bundle.
+            self.new_edges += find_alternate_edges(graph, self.new_edges)
+            if level == 3:
+                # Add CHKs for other bundles to make sure that each
+                # change occurs in at least two CHKS (i.e. edges) if
+                # possible.
+                other_edges, failed = find_redundant_edges(graph,
+                                                           self.new_edges,
+                                                           True)
+                self.new_edges += other_edges
+                for index in failed:
+                    self.parent.ctx.ui_.status("Non-redundant index: %i\n"
+                                               % index)
             for edge in self.new_edges[:]: # Deep copy!
                 if graph.insert_type(edge) == INSERT_HUGE:
                     # User can do this with level == 5
@@ -305,5 +315,5 @@ class InsertingBundles(RequestQueueState
                 graph.add_edge(edge[:2],
                                (graph.get_length(edge), PENDING_INSERT1))
         elif level == 5: # Reinsert big updates.
-            self.new_edges =  get_huge_top_key_edges(graph, True)
+            self.new_edges = get_huge_top_key_edges(graph, True)
             self._check_new_edges("There are no big edges to re-insert.")