Skip to content
This repository has been archived by the owner on Feb 28, 2019. It is now read-only.

update and delete functions now take an optional rule-set-version #59

Merged
merged 2 commits into from
Mar 21, 2018

Conversation

jskelcy
Copy link
Contributor

@jskelcy jskelcy commented Mar 12, 2018

m3ctrl right now relies on tradition CRUD semantics for interacting with rules. With the current implementation, we are not able to do a check and set operation which are very powerful in a distributed systems setting. Right now the underlying storage layer of m3ctl is KV, which provides a check and set method for data with a given version. This diff updates the HTTP/JSON interface to accept a rule-set-version on rule updates and deletes so that we take advantage of KV check and sets.

NOTE:

  • Currently, if you query a single rule it does not return a rule-set-version. This means in order to update a rule and use check and set you would need to query the entire rule set, which does return a rule-set-version, then make your change with the version. I think it is reasonable to have those endpoints return a version number, however, it may be better to add those in a separate PR.

  • In all cases, I have not made the rule-set-version fields required. This will not break API backward compatibility. I am not sure the best way to go forward here. On one hand, we could say that if these fields are omitted then we skip the check and set, but that is potentially dangerous. The other options are to make it required and break back compat, and fix the UI, or create v2 endpoints. I am personally in the create a new version camp, as it will be much less complicated than making it optional while making the API cleaner. The final solution is to break back compat but that should be avoided whenever possible.

@coveralls
Copy link

coveralls commented Mar 12, 2018

Coverage Status

Coverage remained the same at 57.333% when pulling db7f953 on js/version into c892bc2 on master.

"$ref": "#/definitions/MappingRule"
"type": "object",
"properties": {
"rollup-rule": {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be called mapping-rule?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops.

@@ -442,7 +464,7 @@
"200": {
"description": "Ruleset updated",
"schema": {
"$ref": "#/definitions/ApiResponse"
"$ref": "#/definitions/DeleteResponse"
Copy link
Contributor

Choose a reason for hiding this comment

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

A little confused about the DeleteResponse here - why do we need to return the version for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

well, the version is for a rule set and this operation updates the current ruleset snapshot, there will be one less rule. If you then wanted to do further modifications after the delete you would be operating on a stale version of the rule set so it needs to be returned.

@jskelcy jskelcy force-pushed the js/version branch 4 times, most recently from b9d9cf3 to 41dd2c3 Compare March 14, 2018 22:26
@jskelcy
Copy link
Contributor Author

jskelcy commented Mar 14, 2018

Updated this PR with a different approach. I have added a bulk update endpoint where a set of changes to a ruleset can be submitted. There is a flag which will apply all the updates either atomically or not based on the atomic flag. Initially, only atomic operations will be supported. This endpoint accepts a rule-set-verison which will be used as a check and set version number for the ruleset. If the version is stale an error will be returned from the endpoint.

@jskelcy jskelcy force-pushed the js/version branch 3 times, most recently from 571ad04 to be710d3 Compare March 14, 2018 22:41
@jskelcy jskelcy force-pushed the js/version branch 3 times, most recently from fe30074 to 58c4aeb Compare March 14, 2018 23:15
"parameters": [
{
"in": "path",
"name": "namespaceID",
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is probably unnecessary to be required. You are updating the resource which is in the URL, so this can be inferred.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Interesting. I took a look at the other endpoint URIs and it looked like they required parameters that were part of the URL. Perhaps we should keep if for consistency.

Copy link
Collaborator

Choose a reason for hiding this comment

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

If thats the case, then lets keep it consistent

{
"in": "body",
"name": "rule-set-version",
"description": "The ruleset version to update, for check and set operations",
Copy link
Collaborator

Choose a reason for hiding this comment

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

If this isn't passed, will it skip this check-set validation? If so, let's make a comment in the description here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should actually be required. I was thinking that if you did things non-atomically you would not need to pass this, but I was wrong about that, but it should always be required.

"$ref": "#/definitions/ApiResponse"
}
},
"409": {
Copy link
Collaborator

Choose a reason for hiding this comment

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

When would this occur? This is a PUT. This implies that we are creating something via a POST

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A 409 is the case when you provide a rule-set-version that is stale.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Should the description clarify this?

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 see. The description here is a little confusing. But I think a 409 is still relevant.

},
{
"in": "body",
"name": "atomic",
Copy link
Contributor

Choose a reason for hiding this comment

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

Actually on a second thought, we probably don't need this to begin with and always require a ruleset version to start with. We can always relax that constraint later if needed.

@@ -863,20 +936,80 @@
"type": "integer",
"format": "unixMillis"
},
"mappingRules": {
"mappingRulesChanges": {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think these should be mapping rules instead of mapping rule changes?

"type": "array",
"items": {
"$ref": "#/definitions/MappingRule"
}
},
"rollupRules": {
"rollupRulesChanges": {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think these should be rollup rules instead of rollup rule changes?

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 should have caught this. I think this was bad find and replace.

@jskelcy jskelcy force-pushed the js/version branch 2 times, most recently from d099d1a to 6ab39b8 Compare March 16, 2018 13:24
@@ -84,7 +84,7 @@
}
},
"409": {
"description": "Namespace already exists",
"description": "The ruleset got updated while you were looking at it.",
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm I thought we'd return 409 when the namespace already existed? If someone calls this API programmatically without checking whether the namespace exists beforehand, they can get 409 if they try to create a namespace that exists. If so, probably wanna keep it as "Namespace already exists"?

@@ -184,6 +184,72 @@
}
}
},
"/namespaces/{namespaceID}/ruleset/bulk": {
"put": {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be POST rather than PUT since calling this endpoint twice with the same set of changes are not idempotent?

"tags":[
"namespaces"
],
"summary": "Bulk update endpoint for namespace changes",
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: for ruleset changes.

},
{
"in": "body",
"name": "rule-set-version",
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be ruleset-version instead of rule-set-version?

{
"in": "body",
"name": "rule-set-version",
"description": "The ruleset version to update, for check and set operations",
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: might be more clear to say "The ruleset version to atomically apply bulk updates against"

"properties": {
"operation": {
"type": "string",
"enum": ["add", "remove", "change"],
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this mean we might add / delete namespace as part of the bulk operation? Might wanna think about if we want to allow that given that we already have APIs for adding new namespaces and deleting existing namespaces?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was something I was thinking about. It does seem like this endpoint would be more simple if we did not allow for these types of operations, but if users are going to be hitting this endpoint programmatically it might be easier to have a one-stop endpoint for manipulating rules. I can remove it for now and if we need to add it back later we can.

},
"from": {
"$ref": "#/definitions/MappingRuleData",
"description": "old version of rule"
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps better to say "old rule data"?

Also this can be null for "add" and "change" operations I assume?

},
"to": {
"$ref": "#/definitions/MappingRuleData",
"description": "new version of rule"
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps better to say "new rule data"?

Also this can be null for "remove" operations I assume?

},
"from": {
"$ref": "#/definitions/RollupRuleData",
"description": "old version of rule"
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps better to say "old rule data"?

Also this can be null for "add" and "change" operations I assume?

},
"to": {
"$ref": "#/definitions/RollupRuleData",
"description": "new version of rule"
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps better to say "new rule data"?

Also this can be null for "remove" operations I assume?

@jskelcy jskelcy force-pushed the js/version branch 2 times, most recently from d0b55ae to 1a51f57 Compare March 19, 2018 18:44
Copy link
Contributor

@xichen2020 xichen2020 left a comment

Choose a reason for hiding this comment

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

LGTM.

"properties": {
"operation": {
"type": "string",
"enum": ["add", "remove", "change"],
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we please change remove to delete for consistency? That's what we use in m3metrics as well.

@jskelcy jskelcy merged commit 856ed0e into master Mar 21, 2018
@jskelcy jskelcy deleted the js/version branch March 29, 2018 19:01
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants