Salvatore Casolaro created ZOOKEEPER-5090:
---------------------------------------------

             Summary: SnapshotComparer includes ephemeral znodes in comparison 
output
                 Key: ZOOKEEPER-5090
                 URL: https://issues.apache.org/jira/browse/ZOOKEEPER-5090
             Project: ZooKeeper
          Issue Type: Bug
          Components: server
    Affects Versions: 3.10.0
         Environment: Operating system: Windows 11, amd64
Java version: 17.0.12
ZooKeeper version: 3.10.0-SNAPSHOT
Branch: master
Commit: 9102d0be12e66a5c592d8cbf0c6865f68ffa9252
            Reporter: Salvatore Casolaro


h2. Description

SnapshotComparer includes ephemeral znodes in its comparison output, although 
its class documentation states that it only outputs information about permanent 
nodes and ignores both sessions and ephemeral nodes.

The issue was reproduced on the Apache ZooKeeper master branch at commit
9102d0be12e66a5c592d8cbf0c6865f68ffa9252.
h2. Expected behavior

Znodes whose ephemeralOwner is non-zero should be excluded from the comparison 
data used by SnapshotComparer. Consequently, their paths and payload sizes 
should not appear in the reported differences or affect aggregate deltas.

The official ZooKeeper 3.9.5 documentation states:
{quote}This tool only outputs information about permanent nodes, ignoring both 
sessions and ephemeral nodes.
{quote}
Documentation:
[ZooKeeper 3.9.5 
documentation|https://zookeeper.apache.org/doc/r3.9.5/zookeeperTools.html#zkSnapshotComparer]
h2. Actual behavior

When two valid snapshots containing different ephemeral znodes are compared, 
SnapshotComparer reports the ephemeral paths as nodes found only in the left or 
right tree.

The payload sizes of the ephemeral znodes are also included in the aggregate 
size calculations and affect the delta reported for the root node.

Relevant output:
{code:java}
Node  found in both trees. Delta: -2 bytes, 0 descendants
Node /effimero_sinistro found only in left tree. Descendant size: 13. 
Descendant count: 0
Node /ephemeral_test found only in right tree. Descendant size: 11. Descendant 
count: 0
All layers compared.
{code}

h2. Steps to reproduce
# Create a valid snapshot containing an ephemeral znode named 
/effimero_sinistro with a 13-byte payload.
# Create another valid snapshot containing an ephemeral znode named 
/ephemeral_test with an 11-byte payload.
# Verify with SnapshotFormatter that both nodes have a non-zero ephemeralOwner.
# Compare the snapshots using SnapshotComparer with both thresholds set to zero:

{code:java}
-l left_ephemeral.snap -r right_ephemeral.snap -b 0 -n 0
{code}
# Observe that SnapshotComparer reports both ephemeral paths and includes their 
payload sizes in the aggregate root delta.

h2. Snapshot evidence

The dump of the left snapshot confirms:
{code:java}
/effimero_sinistro
ephemeralOwner = 0x10000101fa30000
dataLength = 13

Session:
0x10000101fa30000, timeout = 30000, ephemeralCount = 1
{code}
The dump of the right snapshot confirms:
{code:java}
/ephemeral_test
ephemeralOwner = 0x10000101fa30001
dataLength = 11

Session:
0x10000101fa30001, timeout = 30000, ephemeralCount = 1
{code}
Both ephemeralOwner values are non-zero and correspond to sessions stored in 
the respective snapshots.
h2. Reproducibility

The black-box regression test was first executed three times and failed 
consistently.

The issue was subsequently reproduced on a verification branch based on
upstream/master at the time of testing:
{code:java}
ZooKeeper version: 3.10.0-SNAPSHOT
Branch: master
Commit: 9102d0be12e66a5c592d8cbf0c6865f68ffa9252
{code}
SnapshotComparer completes the comparison successfully, but the regression test 
fails because the output contains the ephemeral znode path:
{code:java}
java.lang.AssertionError:
Output must not contain: /effimero_sinistro
{code}
h2. Possible cause

The issue appears to originate in TreeInfo.TreeNode.populateChildren().

The method retrieves each DataNode and adds it to the internal TreeInfo without 
checking whether the node has a non-zero ephemeralOwner:
{code:java}
DataNode childNode = dataTree.getNode(childPath);
long size;
synchronized (childNode) {
    size = childNode.data == null ? 0 : childNode.data.length;
}

TreeNode childTreeNode = new TreeNode(childPath, size);
childTreeNode.populateChildren(childPath, dataTree, treeInfo, currentDepth + 1);
children.add(childTreeNode);
{code}
As a result, ephemeral znodes are included in the internal comparison tree. 
Their paths can be reported directly, and their payload sizes can propagate to 
ancestor aggregates.

In this reproduction, the 13-byte left ephemeral payload and the 11-byte right 
ephemeral payload produce the observed -2 byte delta for the root node.
h2. Regression test

A black-box JUnit 4 test invokes SnapshotComparer.main(String[]) with the two 
snapshot files. It verifies that the comparison completes successfully but does 
not include either ephemeral path in its output.

The test is currently marked with @Ignore in the complete experimental test 
suite because it exposes the reported defect. The @Ignore annotation was 
temporarily removed when reproducing the failure.

The relevant assertions are:
{code:java}
assertCompletedSuccessfully(result);
assertOutputDoesNotContain(result, LEFT_EPHEMERAL_PATH);
assertOutputDoesNotContain(result, RIGHT_EPHEMERAL_PATH);
{code}
The minimal snapshot files, sanitized snapshot dumps, and the complete
regression test can be provided if needed.
h2. Related issue

SnapshotComparer was originally introduced in ZOOKEEPER-3427.

I searched Jira and the Apache ZooKeeper GitHub repository for existing issues 
and pull requests concerning SnapshotComparer, ephemeral nodes, ephemeralOwner, 
permanent-node filtering, and session filtering, but I did not find an existing 
report covering this behavior.
h2. Academic context

This issue was identified during an academic software testing activity 
supervised by Prof. Guglielmo De Angelis.

GitHub profile:
[Prof. Guglielmo De Angelis on GitHub|https://github.com/gulyx]



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to