Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method to create full key path if it does not exist #2607

Merged
merged 17 commits into from
Oct 4, 2023

Conversation

GrantPSpencer
Copy link
Contributor

@GrantPSpencer GrantPSpencer commented Aug 23, 2023

Issues

  • My PR addresses the following Helix issues and references them in the PR description:

Metaclient currently does not offer the functionality of attempting to create a key's parents if they do not exist.
I'd like to expose a method that does the following:
Assume no nodes have been created
createFullPath("/a/b/c/d", data)
This would then create nodes /a, /b, and /c with null values and lastly create node /d with the data passed in.

Description

  • Here are some details about my PR, including screenshots of any UI changes:

Add functionality to create the full path to a key if the path does not already exist.

Tests

  • The following tests are written for this issue:

Added unit tests to both TestStressZkClient.Java and TestZkMetaClient.java

Changes that Break Backward Compatibility (Optional)

  • My PR contains changes that break backward compatibility or previous assumptions for certain methods or API. They include:

None. Only new method

Documentation (Optional)

  • In case of new functionality, my PR adds documentation in the following wiki page:

No documentation added

Commits

  • My commits all reference appropriate Apache Helix GitHub issues in their subject lines. In addition, my commits follow the guidelines from "How to write a good git commit message":
    1. Subject is separated from body by a blank line
    2. Subject is limited to 50 characters (not including Jira issue reference)
    3. Subject does not end with a period
    4. Subject uses the imperative mood ("add", not "adding")
    5. Body wraps at 72 characters
    6. Body explains "what" and "why", not "how"

Code Quality

  • My diff has been formatted using helix-style.xml
    (helix-style-intellij.xml if IntelliJ IDE is used)

@GrantPSpencer GrantPSpencer changed the title Metaclient Add method to create full key path if it does not exist Aug 23, 2023
Comment on lines 156 to 163
while (--i >= 0) {
if (EntryMode.TTL.equals(mode)) {
createWithTTL(nodePaths.get(i), data, ttl);
} else {
create(nodePaths.get(i), data, i == 0 ? mode : parentMode);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This require try catch that if two threads parallel creation the children at the same time. Also I would imagine this should be same path as previous one.

@xyuanlu could you please help @GrantPSpencer walk through the idea?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I fully understand this race condition yet. I'll sync up with @xyuanlu to get more info

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to ignore when NodeExistsException thrown when we try to create the parents, only throw when we try to create the full node path. This means we don't throw an error if another thread has already created the parents we are trying to create.

}
}

// Reattempt creation of children that failed due to NoNodeException
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update this comment.

}

// Reattempt creation of children that failed due to NoNodeException
while (--i >= 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this i ++ or --I? We should start with 0, right?

Copy link
Contributor Author

@GrantPSpencer GrantPSpencer Sep 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My approach is to use the same counter that we used in the previous loop. I'll walk through it below:
Lets say customer calls recursiveCreate("/first/second/third")
ZK only has "/first" node

This gets split into three separate nodes comprising the chain -->
nodePaths = ["/first/second/third", "/first/second", "/first"]

The first while loop we start with i=0 and increment up (i++) if we don't create it

  • i=0, Check if parent of nodePaths.get(i) -> "/first/second/third" exists, it does not so i++
  • i=1, Check if parent of nodePaths.get(i) -> "/first/second" exists, it exists so create "/first/second" and exit loop

The second while loop then goes back through the list of nodes and attempts to create the ones we missed

  • pre-decrement i, so i=0, create nodePaths.get(i)->"/first/second/third"

This approach of starting from the final child node is my attempt at optimizing over starting from the root node and then working down to the final child node. Unfortunately, my code has bloated quite a bite as a result :/

Also, we could have this as one loop, but I separated it into two as I did not want a possibility where a thread is stuck in an endless loop of incrementing/decrementing due to some unexpected behavior.

Comment on lines 153 to 155
if (i != 0) {
throw e;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes the API complicated behavior. I would suggest to consider if there is exist nodes, then we just stop disregard it is root race condition or not.

Otherwise, the API behavior would be a little bit complicate. We can just let user to retry create it recursively if it is root race condition.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metaclient's normal create behavior is to throw error when creation fails due to NodeExistsException. So should we likewise just let this error be thrown and not catch it? For context, this change was to address your comments here regarding two threads creating children at same time:
#2607 (comment)

@GrantPSpencer
Copy link
Contributor Author

Pull request approved by @junkaixue
Commit message: Add recursiveCreate functionality to metaclient

@xyuanlu xyuanlu merged commit 113b992 into apache:metaclient Oct 4, 2023
5 checks passed
asfgit pushed a commit that referenced this pull request Oct 5, 2023
xyuanlu added a commit that referenced this pull request Oct 16, 2023
* MetaClientCache Part 1 - API's, configs, and builders (#2612)

MetaClientCache Part 1 - API's, configs, and builders

---------

Co-authored-by: mapeng <[email protected]>

* Skip one time listener re-register for exists for ZkClient - MetaClient usage. (#2637)

* Lattice cache - caching just data implementation (#2619)

Lattice cache - caching just data implementation
---------

Co-authored-by: mapeng <[email protected]>

* Add recursiveCreate functionality to metaclient (#2607)



Co-authored-by: Grant Palau Spencer <[email protected]>

* Lattice Children Cache Implementation(#2623)

Co-authored-by: mapeng <[email protected]>

---------

Co-authored-by: Marcos Rico Peng <[email protected]>
Co-authored-by: mapeng <[email protected]>
Co-authored-by: Grant Paláu Spencer <[email protected]>
Co-authored-by: Grant Palau Spencer <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants