Orsiso

780 views
705 views

Published on

Published in: Technology, Business
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
780
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
7
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Orsiso

  1. 1. Asynchronous SQL in Flex Jerome Poudevigne Aureliant CTO
  2. 2. OrSiSo <ul><li>Adobe AIR </li></ul><ul><li>Social network aggregator + multiprotocol chat </li></ul><ul><li>Facebook, Twitter, Flickr, Friendster, Yahoo!, MSN, gTalk… </li></ul>
  3. 3. SQLite use by OrSiSo <ul><li>Data comes from the server </li></ul><ul><li>Stored in SQLite database for fast local access </li></ul>
  4. 4. UI heavy on SELECT
  5. 5. UI heavy on SELECT
  6. 6. UI heavy on SELECT
  7. 7. UI heavy on SELECT
  8. 8. UI heavy on SELECT
  9. 9. OrSiSo - contention on DB <ul><li>The user is browsing a lot of data </li></ul><ul><ul><li>Click on a view icon => UI freeeze on a long SELECT </li></ul></ul><ul><li>The data is updated in the background by querying a server </li></ul><ul><ul><li>100’s of INSERT staitements =>UI freeze too </li></ul></ul>
  10. 10. Synchronous cache update ZZZ…. Data arrives Local cache updated Now you can use it again…
  11. 11. Asynchronous : Why ? “ Because synchronous operations execute in the main execution thread, all application functionality (including refreshing the screen and allowing mouse and keyboard interaction) is paused while the database operation or operations are performed.” For long-running operations this can cause a noticeable pause in the application .
  12. 12. Async data set loading <ul><li>Create a class to send SQL search query </li></ul><ul><li>Make this class load chunk by chunk </li></ul><ul><li>Assign an event every time a chunk is loaded </li></ul>
  13. 13. <ul><li>public class ListLoader extends ArrayCollection </li></ul><ul><li>{ </li></ul><ul><li>protected var stmt:SQLStatement = new SQLStatement(); </li></ul><ul><li>protected var result:SQLResult; </li></ul><ul><li>public function ListLoader(source:Array= null ) </li></ul><ul><li>{ </li></ul><ul><li>super (source); </li></ul><ul><li>} </li></ul><ul><li>public function start (userCacheConn:SQLConnection): void </li></ul><ul><li>{ </li></ul><ul><li>(..) </li></ul><ul><li>stmt.addEventListener(SQLEvent.RESULT, onResult ); </li></ul><ul><li>stmt.text = &quot;SELECT * from c_albums where description <> ''&quot; ; </li></ul><ul><li>stmt.execute(30); </li></ul><ul><li>dispatchEvent( new CollectionEvent(CollectionEventKind.RESET)); </li></ul><ul><li>} </li></ul><ul><li>public function next (): void </li></ul><ul><li>{ </li></ul><ul><ul><li>if (stmt.executing) </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>stmt.next(30); </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul><ul><li>public function onResult (e:SQLEvent): void </li></ul><ul><li>{ </li></ul><ul><li>result = stmt.getResult(); </li></ul><ul><li>dispatchEvent( new CollectionEvent(CollectionEvent.COLLECTION_CHANGE)); </li></ul><ul><li>} </li></ul>
  14. 14. <ul><li>protected var dbReadConn:SQLConnection; </li></ul><ul><li>protected var theList:ListLoader = new ListLoader(); </li></ul><ul><li>[ Bindable ] protected var theData:ArrayCollection= new ArrayCollection(); </li></ul><ul><li>theList.addEventListener(CollectionEvent.COLLECTION_CHANGE,onCollectionChange); </li></ul><ul><li>theList.addEventListener(CollectionEventKind.RESET, onCollectionReset); </li></ul><ul><li>protected function onCollectionChange(e:CollectionEvent): void </li></ul><ul><li>{ </li></ul><ul><ul><li>var d:Array = (e.target as ListLoader).data; </li></ul></ul><ul><ul><li>for ( var ii:int=0;ii<d.length;ii++) </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>theData.addItem(d[ii]); </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>if (!theList.complete) </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>theList.next(); // Or use callLater() </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul><ul><li>trace ( &quot;db is opened a-synchronously&quot; ); </li></ul><ul><li>theList.start(dbReadConn); </li></ul><ul><li><mx:List height=&quot; 100% &quot; width=&quot; 100% &quot; dataProvider=&quot; { theData } ” </li></ul><ul><li>itemRenderer=&quot; AlbumRenderer &quot; /> </li></ul>
  15. 15. Inserting data
  16. 16. Synchronous cache update ZZZ…. Data arrives Local cache updated Now you can use it again…
  17. 17. SQLConnection-synchronous <ul><li>We are used to that </li></ul>try { SQLStatement.exec(“ SOME SQL ”); SQLStatement.exec(“ MORE SQL ”); } catch (…) { }
  18. 18. Asynchronous (100’s of XML items) Update in the background User interface never locked Data arrives
  19. 19. SQLConnection-asynchronous <ul><li>Less familiar </li></ul>Stmt.text=“DO SOME SQL”; stmt.addEventListener(COMPLETE, OnNextStep ); stmt.execute(); … Function onNextStep (e:SQLEvent) { stmt.text=“DO MORE SQL” stmt.addEventListener(COMPLETE, onDone ); stmt.execute(); } Function onDone (e:SQLEvent) { // Whew ! }
  20. 20. Issues <ul><li>How can you write 150 insert statements by using the code above ? </li></ul><ul><li>What if you need transactions ? </li></ul>
  21. 21. Transactions-asynchronous <ul><li>Naïve 1 </li></ul>Connection.beginTransaction(); Stmt.text=“DO SOME SQL”; stmt.execute(); Stmt.text=“DO SOME SQL”; stmt.execute(); Stmt.text=“DO SOME SQL”; stmt.execute(); Connection.commit(); Does not work. In Async mode Flex does not guarantee that SQL execution is in the same order as the Flex lines of code…
  22. 22. Transactions-asynchronous <ul><li>Naïve 2 </li></ul>Connection.beginTransaction(); Function onStarted(..) { Stmt.text=“DO SOME SQL”; stmt.addEventListener(COMPLETE, OnNextStep ); stmt.execute(); } Function onNextStep (e:SQLEvent) { stmt.text=“DO MORE SQL” stmt.addEventListener(COMPLETE, onDone ); stmt.execute(); } Function onDone (e:SQLEvent) { // Whew !! And what if I had 150 of these… }
  23. 23. A solution : StatementList class <ul><li>Encapsulate a loop in a class </li></ul>public class StatementList extends EventDispatcher { public function StatementList( conn:SQLConnection, statements:Object, withTransaction:Boolean= false ) public function execute (): void }
  24. 24. <ul><li>public class StatementList extends EventDispatcher implements ISQLExecutable </li></ul><ul><li>{ </li></ul><ul><li>public var _execStack:Array = new Array(); </li></ul><ul><li>public function StatementList( </li></ul><ul><ul><ul><li>conn:SQLConnection, </li></ul></ul></ul><ul><ul><ul><li>statements:Object , </li></ul></ul></ul><ul><ul><ul><li>withTransaction:Boolean= false ) </li></ul></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><ul><li>_ _execStack.push(statements); </li></ul></ul></ul><ul><ul><ul><li>} </li></ul></ul></ul><ul><li>} </li></ul><ul><li>public function execute (): void </li></ul><ul><li>{ </li></ul><ul><ul><li>trace ( &quot;Beginning transaction&quot; ,text); </li></ul></ul><ul><ul><li>_conn.begin ( null , new Responder( onBeginTransaction , </li></ul></ul><ul><ul><ul><li>onErrorStartingTransaction)); </li></ul></ul></ul><ul><li>} </li></ul><ul><li>protected function onBeginTransaction (e:Object= null ): void </li></ul><ul><li>{ </li></ul><ul><li>_ transactionStarted = true ; </li></ul><ul><li>executeNext (); </li></ul><ul><li>} </li></ul>
  25. 25. <ul><li>protected function executeNext (): void </li></ul><ul><li>{ </li></ul><ul><ul><li>if (_execStack.length > 0) </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>_cStmt = _execStack.shift () as SQLStatement; </li></ul></ul><ul><ul><li>(..); </li></ul></ul><ul><ul><li>_cStmt. addEventListener (SQLEvent.RESULT, onResult , false ,0, true ); </li></ul></ul><ul><ul><li>_cStmt. execute (); </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>else </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>t race ( &quot;Done.Committing transaction &quot; ,text); </li></ul></ul><ul><ul><li>_conn.commit ( new Responder(onConclude, onCannotCommit)); </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul><ul><li>protected function onResult (e:Object= null ): void </li></ul><ul><li>{ </li></ul><ul><ul><li>if (e is SQLEvent) </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>(e.target as EventDispatcher).removeEventListener(SQLEvent.RESULT, onResult); </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>executeNext (); </li></ul><ul><li>} </li></ul>
  26. 26. Still having issues… <ul><li>protected function executeNext (): void </li></ul><ul><li>{ </li></ul><ul><ul><li>if (_execStack.length > 0) </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>_cStmt = _execStack.shift () (..); </li></ul></ul><ul><ul><li>_cStmt. addEventListener (SQLEvent.RESULT, onResult , false ,0, true ); </li></ul></ul><ul><ul><li>_cStmt. execute (); </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul><ul><li>protected function onResult (e:Object= null ): void </li></ul><ul><li>{ </li></ul><ul><ul><li>executeNext (); </li></ul></ul><ul><li>} </li></ul>Function onClickListFriends(..) { Stmt.text=“SELECT FRIENDS WHERE”; stmt.execute (); } Guaranteed locking…
  27. 27. One level (of abstraction) up… <ul><li>Create an execution queue </li></ul><ul><li>Everything is IExecutable </li></ul><ul><li>package com.aureliant.model.cache </li></ul><ul><li>{ </li></ul><ul><li>import flash.events.IEventDispatcher; </li></ul><ul><ul><li>public interface IExecutable extends IEventDispatcher </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><ul><li>function execute(): void ; </li></ul></ul></ul><ul><ul><ul><li>function get canceled():Boolean; </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul>
  28. 28. <ul><li>public class ExecutionQueue </li></ul><ul><li>{ </li></ul><ul><li>protected var _theQueue:Array = new Array(); </li></ul><ul><li>protected var _current:IExecutable; </li></ul><ul><li>public function add (item:IExecutable): void </li></ul><ul><li>{ </li></ul><ul><li>_theQueue.push(item); </li></ul><ul><ul><li>if (_current == null ) //start immediately if empty </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>onExecuteNext(); </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul><ul><li>protected function onExecuteNext (e:Event= null ): void </li></ul><ul><li>{ </li></ul><ul><ul><li>if (_current != null ) </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>_current.removeEventListener(StatementListEvent.FINISHED, onExecuteNext); </li></ul></ul><ul><ul><li>_current = null ; </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>while (_theQueue.length > 0) </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><ul><li>var tmp:IExecutable = _theQueue.shift() as IExecutable ; </li></ul></ul></ul><ul><ul><ul><li>if (!tmp.canceled) </li></ul></ul></ul><ul><ul><ul><li>{ </li></ul></ul></ul><ul><ul><ul><li>_current = tmp; </li></ul></ul></ul><ul><ul><ul><li>break ; </li></ul></ul></ul><ul><ul><ul><li>} </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>if (_current != null ) </li></ul><ul><li>{ </li></ul><ul><li>_current . addEventListener (StatementListEvent.FINISHED, onExecuteNext , false ,0, true ); </li></ul><ul><li>_current.execute (); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  29. 29. <ul><li>public function storeAlerts (rawList:Object, responder:IResponder= null ): void </li></ul><ul><li>{ </li></ul><ul><ul><li>// Create the INSERT statements for the alerts items </li></ul></ul><ul><ul><li>var saver :StatementList = new StatementList(withTransaction); </li></ul></ul><ul><ul><li>for ( var ii:int=0;ii<(theList as ArrayCollection).length; ii++) </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>anItem = (theList as ArrayCollection).getItemAt(ii); </li></ul></ul><ul><ul><li>saver.addSQL ( </li></ul></ul><ul><ul><li>&quot;INSERT OR IGNORE INTO showedalert(guid,alreadyShowed) values ('&quot; + </li></ul></ul><ul><ul><li>anItem.guid+ &quot;',0)&quot; ); </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>ExecutionQueue .instance. add (saver); // Put in the queue </li></ul><ul><li>} </li></ul>
  30. 30. Demo <ul><li>Questions ? </li></ul>

×