Created
March 8, 2015 04:11
-
-
Save BlakeWilliams/a8830e9c82b960696fa7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Elixir Channel UUID's | |
| Elixir Channel's don't provide a way to associate a request with a response, which means it's not possible to *reliably* pair requests to responses. | |
| ## The problem | |
| I'm attempting to write an Ember-Data adapter for Phoenix websockets and Ember-Data is heavily built on promises and on a request/response lifecycle. | |
| For example, when implementing the [find-all] function with the existing way channels are handled we run into a problem, `find-all` expects an array of records or a promise that will resolve into an array of records. | |
| Currently the only way I can imagine doing this with the current implementation is to make requests, return a promise that will (eventually) resolve by using the first response received with a matching event. This is fine in a simple `reply` example, but starts to break down when creating multiple records at the same time (particularly of the same type) or dealing with broadcasts (since multiple users creating records and then being broadcasted could break the order, and cause weird issues). | |
| ### How the RESTAdapter handles this | |
| ```javascript | |
| findAll: function(store, type, sinceToken) { | |
| var query; | |
| if (sinceToken) { | |
| query = { since: sinceToken }; | |
| } | |
| return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query }); | |
| }, | |
| ``` | |
| `this.ajax` will return a promise that will either resolve into an array of objects or reject with the appropriate errors. The only way to handle this now with Phoenix is to return an empty set of data and let event update the data when it's received. Ember-Data uses this promise to determine if the data is loaded (if this is the first request), routes use it to show the loading route until the promise resolves, and it doesn't allow us to determine if there are no records loaded yet or if the returned data was empty (since we have to return something). | |
| ### How I propose phoenix.js would solve this | |
| Here's a high level example of how I think the public API would look: | |
| ```javascript | |
| findAll: function(store, type, sinceToken) { | |
| // phoenix is an instance of Phoenix injected by the Ember container | |
| return new Ember.RSVP.Promise(function(resolve, reject) { | |
| this.phoenix.send(`#{type.typeKey}:index`, {}).then(function(data) { | |
| const responseData = JSON.parse(event.data); | |
| resolve(responseData.payload); | |
| }, function(data) { | |
| //reject with the error | |
| }); | |
| }) | |
| }, | |
| ``` | |
| We could achieve this by generating and sending a UUID in each request and responding with the same UUID in the matching response. | |
| This would make the websocket request/response go from this: | |
| ``` | |
| {"topic":"announcements","event":"announcements:index","payload":{}} | |
| {"topic":"announcements","payload":{"announcements":[]}},"event":"announcements:index"} | |
| ``` | |
| to this: | |
| ``` | |
| {"uuid": "123abc", "topic":"announcements","event":"announcements:index","payload":{}} | |
| {"uuid": "123abc", "topic":"announcements","payload":{"announcements":[]}},"event":"announcements:index"} | |
| ``` | |
| "Under the hood" we could create and return a promise that resolves/rejects once a response with the same UUID is received. | |
| ## Buy Why UUID's? | |
| In IRC it was mentioned that using an `ack` response could solve this issue partially, which is true. This only falls apart when you make multiple requests to the same topic:event at the same time. If some fail and some succeed, how can you reliably determine which requests failed and which succeeded? If the responses come out of order, you're out of luck. | |
| When using a UUID you could store an object of promises with the keys being the UUID's and the promises's `resolve` and `reject` functions being the value. When receiving replies this works great, and when receiving a broadcast we could look for this UUID and resolve it if it exists, otherwise handle it manually. | |
| eg: `{ "123abc": { resolve: resolveFunctionHere, reject: rejectFunctionHere } }` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment