perfact / zodbsync

Dump and restore objects between a ZODB and a serialized file system structure
GNU General Public License v2.0
12 stars 5 forks source link

Changed ordering of children in Ordered Folders not always played back correctly #83

Closed viktordick closed 3 years ago

viktordick commented 3 years ago

Describe the bug When using zodbsync pick to transfer commits to a system, sometimes a changed ordering is not actually transferred to the ZODB, leading to a discrepancy between the git repository and the ZODB (and therefore to a Generic Commit the next time zodbsync record / --commit is called).

To Reproduce Details will follow, but one instance where I recently observed this behavior was as follows:

  1. An Ordered Folder had children A, B, C
  2. A commit added a child called D at the end of the list
  3. What actually ended up in the ZODB was the ordering A, B, D, C.

Expected behavior The ordering as defined in the __meta__ file of the Ordered Folder should also be transferred into the ZODB, if possible (i.e., all the children named there are actually present and all children present are named). If there is a mismatch, the ordering within the named children should at least be established.

There is code that attempts to do this, i.e. write_after_recurse_hook in object_types.py. But I guess it fails to handle some situations correctly.

viktordick commented 3 years ago

Tracked in our internal ticket system under T216053

viktordick commented 3 years ago

Tried to reproduce, the originally described situation actually gives the expected result, but if the commit adds D not at the end of the list, but somewhere in between, it still ends up at the end of the list.

The cause is probably that commands like pick and everything else that uses exec uses --no-recurse and lists the affected paths explicitly. That is,

zodbsync playback /Test

gives the correct result with its children ordered as A, B, D, C, while

zodbsync playback /Test /Test/D --no-recurse

appends the new child at the end. This is also the one that is used by commands like pick and exec.

The reason is as follows: The playback with --no-recurse actually does a loop over its arguments. In the first step, it tries to update the meta file of /Test, which contains a new object in its children list, but that object is not yet present, so it is ignored. In the second step, it creates a new child /Test/D, but does not use any information about where to put this object, so it ends up at the end.