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
Allow access plugins to store per-request state with the auth server. #3286
Comments
ping: @marshall-lee |
I agree with compare and swap approach, option 2 is a bit simpler, because allows to have keys/value stored easily, I wonder if we can make it easy to store primitive types of GRPC as well? E.g. what data will be stored by slack plugin initially? Storing blobs will promote folks to think about all serialization/versioning etc, I wonder if we can make simple things simple here Regarding method, is it possible to reuse |
There is That being said, I'm not sure if there is a significant benefit to supporting GRPC types over, say, providing a helper method that simply marshals/unmarshals plugin data from JSON.
Currently, just two
There is no Idea:In the interest of making this as simple as possible (at least for var data MyData
// unmarshal data from request, returns a correctly configured "update token"
token, err := req.UnmarshalExtData("my-plugin", &data)
if err != nil {
return err
}
// if there was no pre-existing state, the token can encode that too
if token.IsInit() {
data = NewMyData()
}
// make some changes to data...
// send updated data to auth server, and get back a "fresh" update token
token, err = client.UpdateExtData(token, &data)
if err != nil {
return err
}
// token is just data... can be used across multiple client connections
token, err = client2.UpdateExtData(token, &data)
// ... Downside of this is that it effectively pushes plugins towards a very specific workflow when it comes to dealing with their data... upside is that its pretty damn simple. Pass around your token to protect yourself from nasty surprises... if your update fails, reload your state and try again. |
|
Currently, for slackbot both approaches are good enough. Lets go with a simpler blobby solution. Anyway safe partial updates, versioning, etc could be expressed on top of that. |
What's data type will be used for blob? Is it a |
The idea with token is not very clear to me, the previous version with just a setter is much easier as it is only one step to understand vs having tokens and some optimistic locking, I suggest we go back to compare and swap and then just make it easy to add key value string pairs |
Alright, based on the above feedback, I think I've got a design in mind that walks a good balance: Have This is our new type ExtDataSetter struct {
RequestID string
Namespace string
Set map[string]string
Expect map[string]string
} The The Ex: // An update with no `Expect` value behaves like a normal upsert.
client.UpdateExtData(ExtDataSetter {
RequestID: req.ID,
Namespace: config.Namespace,
Set: map[string]string {
"hello":"world",
"spam":"eggs",
}
})
// Delete values, or expect fields to not exist using ""
client.UpdateExtData(ExtDataSetter {
RequestID: req.ID,
Namespace: config.Namespace,
Set: map[string]string {
"unwanted-key":"",
},
Expect: map[string]string {
"unexpected-key":"",
}
})
// Treat it like a patch + optimistic locking system if you want to!
client.UpdateExtData(ExtDataSetter {
RequestID: req.ID,
Namespace: config.Namespace,
Set: map[string]string {
"spam":"eggs",
"version":"3",
},
Expect: map[string]string {
"version":"2",
}
}) |
Looks beautiful, however more polish on the naming of |
How about something like this: client.UpdateRequestExtension(ExtensionUpdateParams {
// ...
}) I'm not sure this achieves perfect sense, but I think its closer to the mark. An alternative to the |
sorry for all the exchange, I think this method is quite important though as all plugins will be using it. I think to make it more user friendly we have to de-generalize it a bit. In essence, what we are doing is setting plugin metadata? Can we reflect that in the name somehow? |
Haha, no problem. I have a real issue erring on the side of generalization, but you're right... How about client.UpdateRequestPluginData(PluginDataUpdateParams {
// ...
}) This feels pretty on the nose. Its the per-request data that belongs to the plugin. EDIT: In this case, it might be worth changing the |
perfect!
…On Fri, Jan 17, 2020 at 5:40 PM Forrest Marshall ***@***.***> wrote:
Haha, no problem. I have a real issue erring on the side of
generalization, but you're right...
How about PluginData:
client.UpdateRequestPluginData(PluginDataUpdateParams {
// ...
})
This feels pretty on the nose. Its the data that belongs to the plugin.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#3286?email_source=notifications&email_token=AD5GI6WJIOBQPLVSFM5GCWLQ6JMZHA5CNFSM4KH2FNY2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJJNKDY#issuecomment-575853839>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AD5GI6SLIJEBW45I2WNNJ5DQ6JMZHANCNFSM4KH2FNYQ>
.
--
You received this message because you are subscribed to the Google Groups
"Tech Operations" group.
To unsubscribe from this group and stop receiving emails from it, send an
email to ***@***.***
|
@fspmarshall looks good to me, also agree on updating the
|
@fspmarshall Perhaps rename And |
Expect communicates the fact that it's an expected value, prerequisite, so I would keep Set and Expect |
- Also addresses #3282 by adding retries for CompareAndSwap on SetAccessRequestState and UpdateAccessRequestPluginData.
- Also addresses #3282 by adding retries for CompareAndSwap on SetAccessRequestState and UpdatePluginData.
- Also addresses #3282 by adding retries for CompareAndSwap on SetAccessRequestState and UpdatePluginData.
- Also addresses #3282 by adding retries for CompareAndSwap on SetAccessRequestState and UpdatePluginData.
- Also addresses #3282 by adding retries for CompareAndSwap on SetAccessRequestState and UpdatePluginData.
Added in #3295 |
- Also addresses #3282 by adding retries for CompareAndSwap on SetAccessRequestState and UpdatePluginData.
Many access plugin implementations will end up needing to store small amounts of per-request information (e.g. API callbacks, tokens, timestamps, and the like). In order to support making plugins stateless, we can allow them to store arbitrary data against individual access requests.
Layout
Because multiple plugins may be running simultaneously, its important that some form of "namespacing" exist to ensure that plugins don't accidentally overwrite one-another's data. Some obvious potential mechanisms are:
Use a mapping of the form
plugin-name -> blob
. Each plugin can store an arbitrary blob with any relevant data in its assigned slot.Use a double-layered mapping (or prefixing convention) of the form
plugin-name -> key -> blob
. Each plugin can update keys within its mapping individually.I'm leaning towards 1 right now, mostly because I think it plays better with the operations described below:
Operations
Given that most plugins will leverage some amount of parallelism, its important that plugin authors have tools to protect themselves from getting their plugins into a bad state due to concurrent writes:
API should distinguish between
create
andupsert
operations so that authors don't accidentally overwrite unexpected state.API should allow authors to protect themselves from concurrent writes (e.g. via
compare-and-swap
).Sketch
This is what I'm leaning towards right now:
The text was updated successfully, but these errors were encountered: