Is there a design pattern to change an interface from synchronous to asynchronous without disturbing the application code that uses the interface?
Specifically: How do you easily take code that makes synchronous calls to a database (meaning that it says "I want all the objects that match some query", and before the next line of your code runs, all the objects are returned and ready for your use), and change that to code that makes asynchronous calls to another process for the data (meaning that you make the call, but you don't get the data, your code continues on without the data, and at some later time you get a call, or an event, from the other process that carries the data you asked for).
Details: This is for a AIR/Flex application using embedded SQLite DB Engine. The UI app occasionally gets database locking errors because it has to access same DB at the same time as a second AIR/Flex helper application that is actually populating the DB. Both apps use synchronous calls to SQLite (synchronously making a series of DB calls is a lot easier than breaking up the method to deal with the boundaries of a series of asynchronous DB calls). Tried all kinds of sleep/retry combinations to prevent this contention, but SQLite does not have record/table locking, any update locks the entire database briefly, and the helper app is doing a lot of fast access while it is building the database. Seems like only sure fix is to only allow DB access from the helper app, and make the UI app go through the helper app. But this requires using LocalConnection remote connection which is asynchronous in nature, and the UI app would have to wait for data to be carried in return LocalConnection calls from the helper app. Oh, and the UI app can be run without the helper apps, so when it doesn't have the second helper app available, the UI app still needs to go direct to the database files... So imagine all the object/DB access of the UI apps are going through the same object persistence module like hibernate. (Actually both apps use the same persistence layer code for all the DB access.)
Possible solution: Could you put a shim layer between the App and the persistence module, that would in one case route calls direct to the persistence module, and in the other case route them to an asynchronous call? Oh, and there are no multiple threads, no mutexes, semaphores, or any of those things because its all ActionScript, which is entirely single threaded. The design pattern of the shim layer secretly holding the UI app code in a wait, until the data comes back and then returning it, is not possible because the event of the UI app code has to come in on the same UI app thread that just made the call....
The big question: Can all the magic of mediating between synchronous/asynchronous DB calls be done in the shim layer while shielding the app code from changes, or is the app code going to have to be converted to asynchronous....
Seems like there is no choice but to turn the app code inside out and make it entirely asynchronous. If the shim layer presents only an asynchronous interface for the app to use, then the shim layer can easily deal with whether it deals with the DB requests locally or sends them to a remote process.
Before I turn everything inside out from synchronous to asynchronous, wanted to see if anyone else had any ideas... I know one way to make the conversion less painful is if there is a routine that requires multiple sequential DB calls to do its job, to push the entire method into the helper app so the helper app accomplishes the whole series of DB calls synchronously, and then returns the result asynchronously to the UI app.
(And for the nontechies who might have read this far, here's a question for you: How's that for an answer to FaceBook's question 'What's on your mind?' This is an example of the kind of things that application designers need to think about when they are writing the software that runs on your computer. Welcome to my exciting world, yes?)
1 Comment
comments.show.hide-
-
Permalink
Add CommentJan 20, 2012
Garnet Chaney
Some responses from Facebook posting: http://www.facebook.com/garnetchaney/posts/316130958425550
Rob Powell That could be a queue that stores a closure with a thread servicing the queue.
Rob Powell Oops, read more. No threads.
Rob Powell I believe actionscript has a timer-like service that could be used for servicing the queue. The atomicity when managing the queue might be tough in that language.
Garnet Chaney The lack of threading is why the application was divided into two applications, a UI viewer application for the user to interact with, and a helper app for the long running operation to build the database that they are viewing. That gives me a sneaky way to get two threads, and keep the UI app responsive. Flash apps internally have extra threads for some tasks (like XML parsing), but the AS programmer has no access to this.... Well here is the issue: Imagine DB code: db.getsome data
Garnet Chaney oops this going to be difficult to express in a facebook comment, enter key causes comment to post...
func loadSeriesOfDisplayObjectsIntoArrayCollection() { db.getsomedata; db.getsomeotherdata; foreach(thing in previous data){ db.anothergetsomedatacall; addDisplayObjectBasedonLastDataToAnArrayCollection; } alldone("TheArrayCollection just built magically (through binding) causes the UI to update") }This UI app routine written to expect those DB calls to be done synchronously to build a list of display objects. But any one of them can fail due to the locking contention with the backend. Changing that method for each db call to be asynchronous (wait for a callback with data before going to the next step) seems painful..... the DB calls already have logic for retry in case of locking error up to 12 times, with randomly increasing wait times (meaning front end thread is stuck, risking UI responsiveness issues), and still sometimes that isn't enough because of how quickly backend is updating the database.
I suppose another approach might be some periodic pauses in the backend updates to allow time for the front end get a chance to access the DB.....