In this talk, I'll show how to combine weak references, hashes, binding and item renderers to produce an elegant solution to the problem of keeping objects synchronized and reducing calls to the server.
Outline:
*
The WeakReference class
o
Allows cached objects to be garbage collected
o
Briefly explain difference between hard references and soft references
*
The EntityWrapper class
o
Gives item renderers an object that they can immediately bind to while potentially waiting for the server to respond
*
The EntityCache
o
Central location for all VO objects currently referenced by the application
o
Used to coordinate updates to client-side data
+
Recursively scan an incoming object for VO objects that can be used to fill/update the cache
+
Looks at all properties, properties of properties, etc
o
When querying for a specific object by its id, first check the cache
+
A cache hit returns the object
+
A cache miss queues a call to the server
+
All cache misses in a given frame are grouped together to reduce server load
o
Deals with duplicate objects
+
If an entity is already represented by an object in the cache, update the existing object's properties and discard the new object
+
Use the cache to make sure there's only ever one authoritative object instance
#
Everything binds to the authoritative instance
#
When that instance gets updated, so does the rest of the application
*
Taking advantage of the behavior of item renderers inside lists
o
Only the visible rows are fetched, makes the app more responsive
o
(Optional) Talk about server-side datagrid sorting to make sure the client doesn't have to have all the data fetched locally
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Zach Pinter - Caching and Synchronization with Flex
1. Caching and
Synchronization in Flex
Zachary Pinter
Senior Developer
EffectiveUI
360Flex
1
2. The Problem
Common application data shown in multiple views, being manipulated by
multiple users at roughly the same time.
2
3. Goals
Fetch entities only once and only as needed.
One definitive instance of a server-side entity.
Update all relevant views when the server sends notice that an entity has
changed.
Server-side agnostic.
3
4. What’s the strategy?
Store all fetched entities in a single cache (Dictionary).
Bind the views to the cache.
4
5. What about memory use?
public class WeakReference Allows cache values to be
{ garbage-collected if they’re not
being referenced anywhere else.
private var dic:Dictionary;
public function WeakReference(obj:*)
{
dic = new Dictionary(true); Text
dic[obj] = 1;
}
public function getValue():* {
for (var item:* in dic) {
return item;
}
return null;
}
}
5
6. The Cache
How do we add an entity to the cache?
public class EntityCache
{
public function updateEntity(entity:BaseVO, ...):BaseVO
{
//...
}
}
6
7. Recurse through properties
Adding a user to the cache also adds its address.
UserVO
id: 1
firstname: “Zachary”
lastname: “Pinter” EntityCache
address: 1: UserVO
AddressVO 2: AddressVO
id: 2
line1: “4444 W 44th Ave”
city: “Denver”
state: “CO”
7
8. Recurse through properties
Arrays behave the same way
UserVO
id: 1
firstname: “Zachary”
lastname: “Pinter”
addresses: [ EntityCache
AddressVO
1: UserVO
id: 2
label: “home” 2: AddressVO
line1: “4444 W 44th Ave” 3: AddressVO
city: “Denver”
state: “CO”
AddressVO
id: 3
label: “work”
line1: “5555 W 55th Ave”
city: “Denver”
state: “CO”]
8
9. Finding an object’s properties
Spring Actionscript (formally Prana)
http://www.pranaframework.org/
var type:Type = Type.forName(classname);
for each (var accessor:Accessor in type.accessors) {
if (accessor.isStatic == false && accessor.access.name == quot;readwritequot;) {
result.push(accessor.name);
}
}
return result;
9
10. Finding an object’s properties
Source generator
public class UserVO extends BaseVO {
public var username : String;
public var firstname : String;
public var lastname : String;
public var address : AddressVO;
override public function getProperties():Array {
return super.getProperties().concat(quot;usernamequot;,quot;firstnamequot;,quot;lastnamequot;,quot;addressquot;);
}
}
10
11. Updating the cache
What if the entity is already in the cache?
EntityCache EntityCache.updateEntity(
1: UserVO(instance A) UserVO(instance B)
id: 1 id: 1
firstname: “Robert” firstname: “Bob”
lastname: “Smith” lastname: “Smith”
)
Copy the properties from
instance B into instance A
EntityCache
Leave instance A in the
1: UserVO(instance A)
cache
id: 1
firstname: “Bob” Discard instance B
lastname: “Smith”
11
12. Updating the cache
What about arrays?
EntityCache
UserVO
id: 1
firstname: “Zachary”
lastname: “Pinter” EntityCache.updateEntity(
addresses: [ AddressVO(instance B)
AddressVO(instance A)
id: 2
id: 2
label: “home” label: “home”
line1: “4444 W 44th Ave” line1: “3333 W 33rd Ave”
city: “Denver” city: “Denver”
state: “CO” state: “CO”
AddressVO )
id: 3
label: “work”
line1: “5555 W 55th Ave”
city: “Denver”
state: “CO”]
12
13. Updating the cache
What about arrays?
UserVO
id: 1 Since we update the existing instance,
firstname: “Zachary” all references to it will see the update.
lastname: “Pinter”
addresses: [
AddressVO(instance A)
id: 2
label: “home”
line1: “3333 W 33rd Ave”
city: “Denver”
state: “CO”
AddressVO
id: 3
label: “work”
line1: “5555 W 55th Ave”
city: “Denver”
state: “CO”]
13
14. Updating the Cache
The flip side
UserVO
EntityCache id: 5
AddressVO(instance A) firstname: “Zachary”
lastname: “Pinter”
id: 2
addresses: [
label: “home” AddressVO(instance B)
line1: “5555 W 55th Ave” id: 2
city: “Denver” label: “home”
state: “CO” line1: “6666 W 66th Ave”
city: “Denver”
state: “CO”
Your cache already has an
AddressVO
AddressVO with id 2 in it, and you
id: 3
add a new UserVO to the cache that
label: “work”
references an updated instance of the
line1: “5555 W 55th Ave”
AddressVO
city: “Denver”
state: “CO”]
14
15. Updating the Cache
UserVO
EntityCache id: 5
AddressVO(instance A) firstname: “Zachary”
lastname: “Pinter”
id: 2
addresses: [
label: “home” AddressVO(instance A)
line1: “6666 W 66th Ave” id: 2
city: “Denver” label: “home”
state: “CO” line1: “6666 W 66th Ave”
city: “Denver”
state: “CO”
The AddressVO in the cache is AddressVO
updated and the AddressVO in id: 3
UserVO.addresses is replaced with label: “work”
the instance from the cache. line1: “5555 W 55th Ave”
city: “Denver”
state: “CO”]
15
16. Updating the Cache
Code looks something like this...
if (obj is Array) {
var arr:Array = obj as Array;
for (var i:int=0;i<arr.length;i++) {
if (arr[i] is BaseVO) {
var res:BaseVO = updateEntity(arr[i] as BaseVO,...);
if (res != null) arr[i] = res;
}
}
}
16
17. Updating the Cache
ArrayCollection’s are slightly trickier
if (obj is ArrayCollection) {
var ac:ArrayCollection = obj as ArrayCollection;
ac.disableAutoUpdate();
for (i=0;i<ac.length;i++) {
if (ac.getItemAt(i) is BaseVO) {
var res:BaseVO = updateEntity(ac.getItemAt(i) as BaseVO,...);
if (res != null) {
ac.setItemAt(res,i);
}
}
}
ac.enableAutoUpdate();
}
17
19. The Cache
How do we get an entity?
What happens if we ask for an entity that isn’t in the cache?
public class EntityCache
{
public function getEntity(id:String):EntityWrapper
{
//...
}
}
19
20. EntityWrapper
[Bindable]
When requesting an entity, a
public class EntityWrapper wrapper object is returned.
{
public var entityId:String; If that object is in the cache,
public var entity:BaseVO; EntityWrapper.entity will have a
value.
public function EntityWrapper(id:String)
{ If the object is not in the cache,
entityId = id;
} EntityWrapper.entity will be null
and a call to fetch the entity will
} be queued.
20
21. Using the Cache
userWrapper = cache.getEntity(userid.text);
<mx:Label text=quot;Username: {userWrapper.entity.username}quot;/>
21
22. Using the Cache - List Renderers
Benefits
‣ Faster initial query since you’re only grabbing the id’s
‣ Rows are lazy-fetched as you scroll
Drawbacks
‣ Sorting has to be handled by the server
22
23. Using the Cache - List Renderers
What’s the code for the item renderer look like?
override public function set data(val:Object):void
{
if(val != data){
if (!val) {
wrapper = null;
} else {
wrapper = cache.getEntity(val);
}
super.data = val;
}
}
23
24. Serverside Considerations
What does this require of your backend infrastructure?
‣ Globally unique keys (or a way to fake it)
‣ Ability to get an entity by its key
‣ Ability to query for entities and only return their keys (used by lazy lists)
Perks:
‣ Versioned entities
‣ Get multiple entities in a single call by passing multiple keys
24
25. Dictionary Keys
Only one cache, need to prevent collisions
Some Options:
‣ Globally unique sequence across all tables (UserVO with id 1, AddressVO with id 2)
‣ UUID’s (great for generating new id’s flex-side)
‣ Combine type with id (“UserVO-1”, “AddressVO-1”)
25