Top Banner
Asynchronous SQL in Flex Jerome Poudevigne Aureliant CTO
30
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Orsiso

Asynchronous SQL in Flex

Jerome Poudevigne

Aureliant

CTO

Page 2: Orsiso

OrSiSo Adobe AIR Social network aggregator +

multiprotocol chat Facebook, Twitter, Flickr, Friendster,

Yahoo!, MSN, gTalk…

Page 3: Orsiso

SQLite use by OrSiSo Data comes from the server Stored in SQLite database for fast local

access

Page 4: Orsiso

UI heavy on SELECT

Page 5: Orsiso

UI heavy on SELECT

Page 6: Orsiso

UI heavy on SELECT

Page 7: Orsiso

UI heavy on SELECT

Page 8: Orsiso

UI heavy on SELECT

Page 9: Orsiso

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

Page 10: Orsiso

Synchronous cache update

ZZZ….

Dataarrives

Local cacheupdated

Now you can use it again…

Page 11: Orsiso

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.

Page 12: Orsiso

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

Page 13: Orsiso

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 = "SELECT * from c_albums where description <> ''"; 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));}

Page 14: Orsiso

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 ("db is opened a-synchronously");theList.start(dbReadConn);

<mx:List height="100%" width="100%" dataProvider="{theData}” itemRenderer="AlbumRenderer" />

Page 15: Orsiso

Inserting data

Page 16: Orsiso

Synchronous cache update

ZZZ….

Dataarrives

Local cacheupdated

Now you can use it again…

Page 17: Orsiso

SQLConnection-synchronous We are used to thattry

{

SQLStatement.exec(“SOME SQL”);

SQLStatement.exec(“MORE SQL”);

}

catch (…)

{

}

Page 18: Orsiso

Asynchronous

(100’s of XML items)

Dataarrives

Update in the background

User interface never locked

Page 19: Orsiso

SQLConnection-asynchronous Less familiarStmt.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 !}

Page 20: Orsiso

Issues How can you write 150 insert

statements by using the code above ? What if you need transactions ?

Page 21: Orsiso

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…

Page 22: Orsiso

Transactions-asynchronous Naïve 2Connection.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… }

Page 23: Orsiso

A solution : StatementList class

public class StatementList extends EventDispatcher{ public function StatementList(

conn:SQLConnection, statements:Object, withTransaction:Boolean=false )

public function execute ():void

}

Encapsulate a loop in a class

Page 24: Orsiso

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 ("Beginning transaction",text);_conn.begin(null, new Responder(onBeginTransaction,

onErrorStartingTransaction));}

protected function onBeginTransaction(e:Object=null):void{_ transactionStarted = true; executeNext();}

Page 25: Orsiso

protected function executeNext():void{

if (_execStack.length > 0){_cStmt = _execStack.shift() as SQLStatement;(..);_cStmt.addEventListener(SQLEvent.RESULT, onResult,false,0,true);_cStmt.execute();}else{

trace ("Done.Committing transaction ",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();}

Page 26: Orsiso

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…

Page 27: Orsiso

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;

}}

Page 28: Orsiso

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(); }}}

Page 29: Orsiso

public function storeAlerts(rawList:Object, responder:IResponder=null):void{

// Create the INSERT statements for the alerts itemsvar saver:StatementList = new StatementList(withTransaction);

for (var ii:int=0;ii<(theList as ArrayCollection).length; ii++){ anItem = (theList as ArrayCollection).getItemAt(ii); saver.addSQL( "INSERT OR IGNORE INTO showedalert(guid,alreadyShowed) values ('"+

anItem.guid+"',0)");}

ExecutionQueue.instance.add(saver); // Put in the queue}

Page 30: Orsiso

Demo Questions ?