Skip to content

Instantly share code, notes, and snippets.

@jvantuyl
Created April 16, 2009 20:21
Show Gist options
  • Save jvantuyl/96630 to your computer and use it in GitHub Desktop.
Save jvantuyl/96630 to your computer and use it in GitHub Desktop.
Use-Case:
Rails is built around the stateless web-page model. This is really at odds with Vertebra's Use-Case, long-running operations. If it takes three minutes to execute a /vm/reboot(env=/env/45) operation, it won't do to block the web-request for the whole time. The way that web-applications handle these things is with AJAX requests that monitor whatever long-running operation is happening on the backend.
Solution:
To accommodate the needs of AJAX requests, we need an agent that acts as a surrogate for the web-application, while providing a friendly polling API. Since Vertebra operations are run from a specific point, we also need to consider that this gateway may require some mechanism to sort out which gateway is being used (at least in the event that multiple gateways are being used in parallel for availability and load-distribution).
Design:
The web-gateway-actor is tailored towards using ActiveResource. As such, we have a API that fits the model of Find, Create, Update, Delete using a JSON format. In this model, our primary object is the "job".
Workflow:
Finding, Creating, Updating, and Deleting a job are pretty self explanatory in the ActiveResource API. So we basically offer finding, creating, updating, and deleting using GET (to /jobs.json), POST (to /jobs.json, redirecting to /jobs/<token>.json), PUT (to /jobs/<token>.json), and DELETE (to /jobs/<token>.json).
Finding results is done using /jobs/<token>/results.json. The default behavior is to block until all results are received then return them in their entirety. For users that wish to poll, they may specify the URL parameter poll=1 which will give them partial results. A poll=1 request to a finalized operation that has no more results should return a 404 error.
Results may be deleted before the operation is done.
Data Structures:
Jobs are just a hash. The key "operation" contains the op. The key "scope" contains the scope. The key "parameters" contains the parameters. The key "expire" contains the amount of time after the job is finalized that it may be expired. All of these keys are mandatory.
Due to the limitations of json, all strings in the parameters hash may be prefixed with "<prefix>:" to specify type information. "res:" indicates that they are a resource. "str:" if they are a string. The latter only exists as a way to escape prefixes, for example "str:res:/why/would/someone/do/this". If your string contains a colon, you should always use the "str:" prefix to make it unambiguous.
Results are returned with the same format as params.
FIXME: Should we offer XML as well? It would avoid the string escaping problem because you can do your own type annotations, although they shouldn't be Ruby/ActiveResource specific. JSON fits the target audience better, so I'm torn as to which is better at the moment. We'll do JSON first.
Authorization:
All requests should require a username and password of the Vertebra identity and password. Right now, these should just be in the config file and checked to see if they match. Eventually this should get a Vertebra Identity, whenever we figure out what those look like.
Notes:
Deleting a running job should abort it. Deleting results should just remove them from the result list. Eventually we should scope the find methods just to the logged-in user (when we have that concept nailed down).
Example:
If this all goes well, an ActiveResource user should be able to do this:
# POST /jobs.xml
thejob = Job.new :operation => "/foo/bar", scope => :single, params => { :baz => 'res:/qux' }, expire=3600
thejob.save
# redirects to /job/1234.xml
# GET /jobs/1234/results.xml
# Assuming has_many works on ActiveResource, can't get a straight answer here
thejob.results.collect { |res| ...do something... }
# If that doesn't work, then perhaps something like:
results = Result.find(:all,params => {:job_id => thejob.id} )
# Assuming this can be made to hit the correct URL, which the docs imply is possible
thejob.destroy
# DELETE /jobs/1234.xml
That would be very cool.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment