-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Rest Endpoint to Create ZNode #12497
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #12497 +/- ##
============================================
- Coverage 61.75% 61.74% -0.01%
Complexity 207 207
============================================
Files 2436 2450 +14
Lines 133233 133550 +317
Branches 20636 20692 +56
============================================
+ Hits 82274 82463 +189
- Misses 44911 44985 +74
- Partials 6048 6102 +54
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
data = payload; | ||
} | ||
if (StringUtils.isEmpty(data)) { | ||
throw new ControllerApplicationException(LOGGER, "Must provide data through query parameter or payload", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel it's ok to create an empty znode. You can put a warning for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd not want to change the behaviour of the existing API right now.
Edit: I mean if we are making changes to the existing /zk/put
API.
if (result) { | ||
return new SuccessResponse("Successfully updated path: " + path); | ||
} else { | ||
throw new ControllerApplicationException(LOGGER, "ZNode already exists at path: " + path, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how do you know is due to znode already exists?
Shall we check the znode existence first then throw or check znode exist inside the else then throw?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_pinotHelixResourceManager.createZKNode(path, znRecord, accessOption, ttl);
returns a false if the node already exists, can't think of other scenarios in which it would return a false. But sure we can check for the existence of the path first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added explicit path check
@ApiResponse(code = 200, message = "Success"), @ApiResponse(code = 204, message = "No Content"), | ||
@ApiResponse(code = 400, message = "Bad Request"), @ApiResponse(code = 500, message = "Internal server error") | ||
}) | ||
public SuccessResponse createNode( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pretty much the same as putData API, except ttl option.
Should you add a new parameter like force
default to true
into putData
API instead of creating a new one?
You can add a new exception into it like NodeAlreadyExist
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This complicates the existing API. Now we have 1. expectedVersion
which is not required for creating new node. 2. ttl
which is not required to update a new node. Not to mention the POST vs PUT semantics would also be incorrect. Wdyt?
Response.Status.INTERNAL_SERVER_ERROR, e); | ||
} | ||
if (result) { | ||
return new SuccessResponse("Successfully updated path: " + path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: updated -> created
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
public SuccessResponse createNode( | ||
@ApiParam(value = "Zookeeper Path, must start with /", required = true) @QueryParam("path") String path, | ||
@ApiParam(value = "Content") @QueryParam("data") @Nullable String data, | ||
@ApiParam(value = "ttl", defaultValue = "-1") @QueryParam("ttl") @DefaultValue("-1") int ttl, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we please add an explanation: -1 means no ttl, and positive values means ephemeral znode
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ephemeral actually is controlled by the access option, not the ttl. TTLs only make sense for pesistent znodes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will add an explanation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
||
boolean result; | ||
try { | ||
result = _pinotHelixResourceManager.createZKNode(path, znRecord, accessOption, ttl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel that we should check the range of ttl before making the call (or maybe check the range inside the createZKNode method)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There isn't a range of tll specified in the zk client. -1L means it's not set, any positive value means it's the TTL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if the value is -2L? Will it treat it as not set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With -2, node creation fails with an ISE, but I'll put a range check to make it clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
}) | ||
public SuccessResponse createNode( | ||
@ApiParam(value = "Zookeeper Path, must start with /", required = true) @QueryParam("path") String path, | ||
@ApiParam(value = "Content") @QueryParam("data") @Nullable String data, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have both data
and payload
? Should it always be set with payload
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This signature was just copied from the zk/put
API as I want to keep them similar. But we can def do with just payload.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that keep payload
only for now. We can add data
if that is really useful, but removing it is much harder.
Response.Status.BAD_REQUEST); | ||
} | ||
//check if node already exists | ||
if (_pinotHelixResourceManager.getZKStat(path) != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this check? Will createZKNode()
throw exception when the node already exists? We should rely on ZK to handle conflict, or there will be potential race condition
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was put in to explicitly detect if a path already exists. ZK does not return any error codes to detect it. Yes, a race condition exists, but that should be fine. Two cases:
- This check passes but node exists by the time ZK tries to create this node: It'll fail anyway transparently to the user (with an ISE instead of Bad Request though, which should be okay as the user would retry).
- This check fails but node is deleted by another process before user receives a response: This is possible even if we leave this check to ZK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we reverse this check? When creation fails, we check if it exists. This should give better handling, and in most cases we can reduce one ZK access
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's a good idea. Flipped it.
}) | ||
public SuccessResponse createNode( | ||
@ApiParam(value = "Zookeeper Path, must start with /", required = true) @QueryParam("path") String path, | ||
@ApiParam(value = "Content") @QueryParam("data") @Nullable String data, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that keep payload
only for now. We can add data
if that is really useful, but removing it is much harder.
Response.Status.BAD_REQUEST); | ||
} | ||
//check if node already exists | ||
if (_pinotHelixResourceManager.getZKStat(path) != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we reverse this check? When creation fails, we check if it exists. This should give better handling, and in most cases we can reduce one ZK access
pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/ZookeeperResource.java
Outdated
Show resolved
Hide resolved
…/resources/ZookeeperResource.java
The only way to create a znode right now via the controller API is using
/zk/put
. However, this overwrites any previous data already existing at the given path. There are certain use-cases in which we want to fail creation of a node if it already exists. For example, creating an ephemeral znode at a path to acquire a mutex. For such cases, we want to create the lock atomically.Behaviour:
This endpoint will return an Bad Request if a node already exists (with the corresponding error message).
Note: For TTL nodes, enable this
System.setProperty("zookeeper.extendedTypesEnabled", "true")
else it'll throw aKeeperException: UNIMPLEMENTED
.