• Like
Orsiso
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

Orsiso

  • 632 views
Published

 

Published in Technology , Business
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
632
On SlideShare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
6
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

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