-
Notifications
You must be signed in to change notification settings - Fork 191
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
feat(schema): Support "key" in throttle schema and the scope of "action" #757
Conversation
packages/schema/docs/build/schema.md
Outdated
@@ -2080,14 +2080,24 @@ Key | Required | Type | Description | |||
--- | -------- | ---- | ----------- | |||
`window` | **yes** | `integer` | The timeframe, in seconds, within which the system tracks the number of invocations for an action. The number of invocations begins at zero at the start of each window. | |||
`limit` | **yes** | `integer` | The maximum number of invocations for an action, allowed within the timeframe window. | |||
`scope` | no | `array`[`string` in (`'user'`, `'auth'`, `'account'`)] | The granularity to throttle by. You can set the scope to one or more of the following: 'user' - Throttles based on user ids. 'auth' - Throttles based on auth ids. 'account' - Throttles based on account ids for all users under a single account. By default, throttling is scoped to the account. | |||
`key` | no | `string` | The key to throttle with in combination with the scope. This should be unique to the operation. While actions of different integrations with the same key and scope will never share the same limit, actions of the same integration with the same key and scope will do when "action" is not in the scope. User data provided for the input fields can be used in the key with the use of the curly braces referencing. For example, to access the user data provided for the input field "test_field", use `{{bundle.inputData.test_field}}`. Note that a required input field should be referenced to get user data always. |
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.
Is it worth mentioning what happens if the key is not provided?
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.
That may be helpful, although implied with "in combination with the scope".
Perhaps even more helpful would be to explain key
and scope
together in the description of the schema instead of here individually in the props.
We could explain that throttling will be shared by all actions with the same key
and that the values referenced by scope
are mixed into it, and that scope
defaults to ['account']
.
Pseudo code may also help here:
const usedKey = (scope || ['account'])
.map(s => values[s])
.concat(key, app)
.join('-');
Same likely applies to LockObjectSchema
.
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.
Updated the descriptions by moving the implication of same key, window, and scope to the schema description.
@@ -19,11 +19,17 @@ module.exports = makeSchema({ | |||
'The maximum number of invocations for an action, allowed within the timeframe window.', | |||
type: 'integer', | |||
}, | |||
key: { | |||
description: | |||
'The key to throttle with in combination with the scope. This should be unique to the operation. While actions of different integrations with the same key and scope will never share the same limit, actions of the same integration with the same key and scope will do when "action" is not in the scope. User data provided for the input fields can be used in the key with the use of the curly braces referencing. For example, to access the user data provided for the input field "test_field", use `{{bundle.inputData.test_field}}`. Note that a required input field should be referenced to get user data always.', |
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 the user not provide a key?
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 happens in the scenario where a user uses the same key in two actions, but has different values?
For example:
update_row: {
...
throttle: {
key: 'row_level_action',
limit: 50,
window: 60,
scope: ['account']
}
}
delete_row: {
...
throttle: {
key: 'row_level_action',
limit: 1000,
window: 60,
scope: ['account']
}
}
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.
Good question!
window
is mixed into thekey
, so if those don't match they won't affect each other. That should be called out in the above description (comment).limit
is applied per action, so if in your examplewindow
would be identical, then within that windowupdate_row
would throttle at 50, butdelete_row
would happily continue up to 1000. 🤔
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 the user not provide a key?
Yes, the key
is not required.
What happens in the scenario where a user uses the same key in two actions, but has different values?
I didn't observe that the window
is mixed into the key
as @FokkeZB pointed out. Responding to the example, using the new information, both actions would share the same final key having the same key
, scope
excluded of "action", and window
. With both using different limits, the action to first run an invocation within each 60s timeframe would determine the same limit that would be applied to both actions' invocations until the 60s timeframe is completed. So both limits of 50
and 1000
could be used but not both in the same 60s timeframe.
To prevent this uncertainty/complication, perhaps we mix the limit
in the final key
?
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 see - I think there's 3 ways to go about this then:
- Remove
window
from key as Fokke suggested - Add
limit
to the key as Kola suggested - Add a validation assertion to this schema: if a throttle key is defined, the limit and key must be equal and when the user runs
zapier validate
they will see a validation error.
1 and 2 carry the risk of unintentional behaviour, where a user may report that they thought their action will throttle at X/Y but is actually doing A/B. But we can be very loud about documenting this behaviour to try and mitigate this.
While 3 carries more work and effort, I think this will lead to implementing the original intent behind adding a throttle key.
I'm fine with any - if 1/2, then I suggest adding a clear message and maybe a snippet of the above example
@FokkeZB @kola-er wdyt?
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.
That seems reasonable, thanks for discussing 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 am happy to move forward with adding the limit
value to the final key
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.
Okay, will include it in the backend code and update the docs here too. Thanks @standielpls
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.
@standielpls Updated the description to note that all the configurations are combined to form the final key in the last commit.
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 makes sense. Thanks all.
Also see https://gitlab.com/zapier/zapier/-/merge_requests/56351#note_1830368072
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.
Approving, but left a comment to improve docs, And @standielpls raised an excellent point that also should be documented or perhaps considered as change (dropping window
from the key?)
packages/schema/docs/build/schema.md
Outdated
@@ -2080,14 +2080,24 @@ Key | Required | Type | Description | |||
--- | -------- | ---- | ----------- | |||
`window` | **yes** | `integer` | The timeframe, in seconds, within which the system tracks the number of invocations for an action. The number of invocations begins at zero at the start of each window. | |||
`limit` | **yes** | `integer` | The maximum number of invocations for an action, allowed within the timeframe window. | |||
`scope` | no | `array`[`string` in (`'user'`, `'auth'`, `'account'`)] | The granularity to throttle by. You can set the scope to one or more of the following: 'user' - Throttles based on user ids. 'auth' - Throttles based on auth ids. 'account' - Throttles based on account ids for all users under a single account. By default, throttling is scoped to the account. | |||
`key` | no | `string` | The key to throttle with in combination with the scope. This should be unique to the operation. While actions of different integrations with the same key and scope will never share the same limit, actions of the same integration with the same key and scope will do when "action" is not in the scope. User data provided for the input fields can be used in the key with the use of the curly braces referencing. For example, to access the user data provided for the input field "test_field", use `{{bundle.inputData.test_field}}`. Note that a required input field should be referenced to get user data always. |
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.
That may be helpful, although implied with "in combination with the scope".
Perhaps even more helpful would be to explain key
and scope
together in the description of the schema instead of here individually in the props.
We could explain that throttling will be shared by all actions with the same key
and that the values referenced by scope
are mixed into it, and that scope
defaults to ['account']
.
Pseudo code may also help here:
const usedKey = (scope || ['account'])
.map(s => values[s])
.concat(key, app)
.join('-');
Same likely applies to LockObjectSchema
.
@@ -19,11 +19,17 @@ module.exports = makeSchema({ | |||
'The maximum number of invocations for an action, allowed within the timeframe window.', | |||
type: 'integer', | |||
}, | |||
key: { | |||
description: | |||
'The key to throttle with in combination with the scope. This should be unique to the operation. While actions of different integrations with the same key and scope will never share the same limit, actions of the same integration with the same key and scope will do when "action" is not in the scope. User data provided for the input fields can be used in the key with the use of the curly braces referencing. For example, to access the user data provided for the input field "test_field", use `{{bundle.inputData.test_field}}`. Note that a required input field should be referenced to get user data always.', |
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.
Good question!
window
is mixed into thekey
, so if those don't match they won't affect each other. That should be called out in the above description (comment).limit
is applied per action, so if in your examplewindow
would be identical, then within that windowupdate_row
would throttle at 50, butdelete_row
would happily continue up to 1000. 🤔
feat(schema): Support "key" in throttle schema and the scope of "action"