Thread (43 messages) 43 messages, 6 authors, 2012-08-01

Re: [RFC PATCH 5/7] Btrfs: add btrfs_compare_trees function

From: Alexander Block <hidden>
Date: 2012-07-04 20:18:35

On Wed, Jul 4, 2012 at 9:13 PM, Alex Lyakas
[off-list ref] wrote:
Hi Alex,
quoted
+static int tree_compare_item(struct btrfs_root *left_root,
+                            struct btrfs_path *left_path,
+                            struct btrfs_path *right_path,
+                            char *tmp_buf)
+{
+       int cmp;
+       int len1, len2;
+       unsigned long off1, off2;
+
+       len1 = btrfs_item_size_nr(left_path->nodes[0], left_path->slots[0]);
+       len2 = btrfs_item_size_nr(right_path->nodes[0], right_path->slots[0]);
+       if (len1 != len2)
+               return 1;
+
+       off1 = btrfs_item_ptr_offset(left_path->nodes[0], left_path->slots[0]);
+       off2 = btrfs_item_ptr_offset(right_path->nodes[0],
+                               right_path->slots[0]);
+
+       read_extent_buffer(left_path->nodes[0], tmp_buf, off1, len1);
+
+       cmp = memcmp_extent_buffer(right_path->nodes[0], tmp_buf, off2, len1);
+       if (cmp)
+               return 1;
+       return 0;
+}
It might be worth to note in the comment, that tmp_buff should be
large enough to hold the item from the left tree. Can it happen that
the right tree has a different leafsize?
This function is only to be used for for the tree compare function and
there we allocate a buffer of root->leafsize, so definitely all items
should fit. As far as I know, Chris (please correct me if I'm wrong)
once guaranteed that ALL trees in a FS will have the same leaf size
and this will ever be the case.
quoted
+       /*
+        * Strategy: Go to the first items of both trees. Then do
+        *
+        * If both trees are at level 0
+        *   Compare keys of current items
+        *     If left < right treat left item as new, advance left tree
+        *       and repeat
+        *     If left > right treat right item as deleted, advance right tree
+        *       and repeat
+        *     If left == right do deep compare of items, treat as changed if
+        *       needed, advance both trees and repeat
+        * If both trees are at the same level but not at level 0
+        *   Compare keys of current nodes/leafs
+        *     If left < right advance left tree and repeat
+        *     If left > right advance right tree and repeat
+        *     If left == right compare blockptrs of the next nodes/leafs
+        *       If they match advance both trees but stay at the same level
+        *         and repeat
+        *       If they don't match advance both trees while allowing to go
+        *         deeper and repeat
+        * If tree levels are different
+        *   Advance the tree that needs it and repeat
+        *
+        * Advancing a tree means:
+        *   If we are at level 0, try to go to the next slot. If that's not
+        *   possible, go one level up and repeat. Stop when we found a level
+        *   where we could go to the next slot. We may at this point be on a
+        *   node or a leaf.
+        *
+        *   If we are not at level 0 and not on shared tree blocks, go one
+        *   level deeper.
+        *
+        *   If we are not at level 0 and on shared tree blocks, go one slot to
+        *   the right if possible or go up and right.
+        */
According to the strategy and to the code later, "left" tree is
treated as "newer one", while "right" as "older one", correct? Do you
think it would be more intuitive to make it the other way around,
although I guess this is a matter of personal taste. I had to draw the
leafs reversed to keep going:
R       L
-----     -----
| | | |     | | | |
-----     -----
To be honest...I always preferred the way you suggested in the past
when I thought about compares. But for some reason, I didn't even
think about that and just implemented that function in single
flow...it took days until I've even noticed that I swapped left/right
in my head :D I now would like to stay with that, as all the btrfs
send code uses left/right in this way and I never had the problem with
mixing that up again. If people like, I have nothing against changing
that later if someone wants to, but that's nothing I would like to do
myself.
quoted
+               if (advance_left && !left_end_reached) {
+                       ret = tree_advance(left_root, left_path, &left_level,
+                                       left_root_level,
+                                       advance_left != ADVANCE_ONLY_NEXT,
+                                       &left_key);
+                       if (ret < 0)
+                               left_end_reached = ADVANCE;
+                       advance_left = 0;
+               }
+               if (advance_right && !right_end_reached) {
+                       ret = tree_advance(right_root, right_path, &right_level,
+                                       right_root_level,
+                                       advance_right != ADVANCE_ONLY_NEXT,
+                                       &right_key);
+                       if (ret < 0)
+                               right_end_reached = ADVANCE;
+                       advance_right = 0;
+               }
Do you think it's worth it to put a check/warning/smth before that,
that either advance_right or advance_left is non-zero, or we have
reached ends in both trees?
Having the left or right end reached before the other sides end is
reached is something that is completely normal and expected.
quoted
+               } else if (left_level == right_level) {
...
quoted
+               } else if (left_level < right_level) {
+                       advance_right = ADVANCE;
+               } else {
+                       advance_left = ADVANCE;
+               }
Can you pls explain why it is correct?
Why if we are on lower level in the "newer" tree than we are in the
"older" tree, we need to advance the "older" tree? I.e., why this
implies that we are on the lower key in the "older" tree? (And
vice-versa). I.e., how difference in levels indicates relation between
keys?
Difference in levels has no relation to the keys. These advances
basically try to keep the two trees positions "in-sync". The compare
always tries to get both trees to a point where they are at the same
level, as only then we can compare keys. Also, the two trees may have
different root levels, this code also handles that case.
Thanks,
  Alex.
Thanks for the review :)
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help