lib: Refresh new graph with received values from previous graph

This pulls in the Send/Recv values from the previous graph so that our
Cmp functions are more likely to not remake resources that should
otherwise not have changed. Unnecessary remakes can destroy the private
state of a resource which can make certain operations impossible.
This commit is contained in:
James Shubin
2024-01-20 03:34:05 -05:00
parent 1630eafbe3
commit 6761984f2c

View File

@@ -731,6 +731,76 @@ func (obj *Main) Run() error {
continue continue
} }
// XXX: Should this run earlier or later than here?
// run Send/Recv on the new graph with data from the old
// graph, so that we won't need to unnecessarily re-make
// a resource that had previously received some data and
// is now different than the equivalent resource in this
// new incoming graph!
if err := obj.ge.Apply(func(g *pgraph.Graph) error { // apply runs on nextGraph (new)
old := obj.ge.Graph()
if old.NumVertices() == 0 { // skip initial empty graph
return nil
}
mapped, err := engine.ResGraphMapper(old, g) // (map[engine.RecvableRes]engine.RecvableRes, error)
if err != nil {
return err
}
for _, v := range g.Vertices() {
res, ok := v.(engine.RecvableRes)
if !ok {
continue // we'll catch the error later!
}
if obj.Flags.Debug {
Logf("SendRecv: %s", res) // receiving here
}
// This mapping function is used to
// replace the Recv() function that is
// called in Send/Recv so that our new
// resources in the graph we're about to
// graphsync on can use the Recv() func
// from the current (possibly stale)
// resources so that they have the
// current values they've already
// received. This is needed so that the
// compare doesn't fail unnecessarily if
// the new resource doesn't happen to
// have the field value as whatever the
// older one previously received. It is
// important to not remake resources
// unnecessarily because doing so resets
// any important private struct fields
// that they might have.
fn := func(r engine.RecvableRes) (map[string]*engine.Send, error) {
old, exists := mapped[r] // r is new
if !exists { // initial graph could be empty
// possible programming error?
//return nil, fmt.Errorf("could not find a match for %p %s", r, r)
//return r.Recv(), nil // NO!
return map[string]*engine.Send{}, nil
}
return old.Recv(), nil // swap
}
if updated, err := graph.SendRecv(res, fn); err != nil {
return errwrap.Wrapf(err, "could not SendRecv")
} else if as := graph.UpdatedStrings(updated); len(as) > 0 {
for _, s := range as {
Logf("SendRecv: %s", s)
}
}
}
return nil
}); err != nil { // apply an operation to the new graph
obj.ge.Abort() // delete graph
Logf("error applying operation to the new graph: %+v", err)
continue
}
// Double check before we commit. // Double check before we commit.
if err := obj.ge.Apply(func(graph *pgraph.Graph) error { if err := obj.ge.Apply(func(graph *pgraph.Graph) error {
_, e := graph.TopologicalSort() // am i a dag or not? _, e := graph.TopologicalSort() // am i a dag or not?