Changed Key object management and database fetching strategy. Parallel fetching is now allowed, but at cache insertion strict checks are performed.
This commit is contained in:
parent
8451bfddd9
commit
e20aaec618
3 changed files with 72 additions and 64 deletions
|
@ -4,7 +4,7 @@
|
|||
package helma.objectmodel;
|
||||
|
||||
|
||||
import Acme.LruHashtable;
|
||||
import helma.util.CacheMap;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
|
@ -13,32 +13,20 @@ import java.io.Serializable;
|
|||
* key of the node and unique within each HOP application. Currently only
|
||||
* single keys are supported.
|
||||
*/
|
||||
public class Key implements Serializable {
|
||||
public final class Key implements Serializable {
|
||||
|
||||
protected String type;
|
||||
protected String id;
|
||||
private int hash;
|
||||
private static LruHashtable keycache;
|
||||
|
||||
|
||||
public synchronized static Key makeKey (DbMapping dbmap, String id) {
|
||||
String _type = dbmap == null ? "" : dbmap.typename;
|
||||
String _id = id.trim (); // removed .toLowerCase() - hw
|
||||
return makeKey (_type, _id);
|
||||
public Key (DbMapping dbmap, String id) {
|
||||
this.type = dbmap == null ? "" : dbmap.typename;
|
||||
this.id = id;
|
||||
hash = this.id.hashCode ();
|
||||
}
|
||||
|
||||
private synchronized static Key makeKey (String _type, String _id) {
|
||||
if (keycache == null)
|
||||
keycache = new LruHashtable (1000, 0.9f);
|
||||
Key k = (Key) keycache.get (_type+"#"+_id);
|
||||
if (k == null) {
|
||||
k = new Key (_type, _id);
|
||||
keycache.put (_type+"#"+_id, k);
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
private Key (String type, String id) {
|
||||
public Key (String type, String id) {
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
hash = this.id.hashCode ();
|
||||
|
@ -66,8 +54,7 @@ public class Key implements Serializable {
|
|||
* a key that can't be mistaken for a relational db key.
|
||||
*/
|
||||
public Key getVirtualKey (String sid) {
|
||||
Key virtkey = makeKey ("", getVirtualID (type, id, sid));
|
||||
return virtkey;
|
||||
return new Key ("", getVirtualID (type, id, sid));
|
||||
}
|
||||
|
||||
public static String getVirtualID (DbMapping pmap, String pid, String sid) {
|
||||
|
|
|
@ -96,6 +96,7 @@ public class Node implements INode, Serializable {
|
|||
transient INode cacheNode;
|
||||
transient WrappedNodeManager nmgr;
|
||||
transient DbMapping dbmap;
|
||||
transient Key primaryKey = null;
|
||||
transient DbMapping parentmap;
|
||||
transient String subnodeRelation = null;
|
||||
transient long lastSubnodeFetch = 0;
|
||||
|
@ -489,6 +490,7 @@ public class Node implements INode, Serializable {
|
|||
public void setDbMapping (DbMapping dbmap) {
|
||||
if (this.dbmap != dbmap) {
|
||||
this.dbmap = dbmap;
|
||||
primaryKey = null;
|
||||
((Transactor) Thread.currentThread()).visitCleanNode (this);
|
||||
}
|
||||
}
|
||||
|
@ -498,7 +500,9 @@ public class Node implements INode, Serializable {
|
|||
}
|
||||
|
||||
public Key getKey () {
|
||||
return Key.makeKey (dbmap, id);
|
||||
if (primaryKey == null)
|
||||
primaryKey = new Key (dbmap, id);
|
||||
return primaryKey;
|
||||
}
|
||||
|
||||
public void setSubnodeRelation (String rel) {
|
||||
|
@ -1249,7 +1253,7 @@ public class Node implements INode, Serializable {
|
|||
parent.unset (oldvalue);
|
||||
parent.addNode (this);
|
||||
// let the node cache know this key's not for this node anymore.
|
||||
nmgr.evictKey (Key.makeKey (prel.other, prel.getKeyID (parent, oldvalue)));
|
||||
nmgr.evictKey (new Key (prel.other, prel.getKeyID (parent, oldvalue)));
|
||||
}
|
||||
}
|
||||
parent.setNode (value, this);
|
||||
|
@ -1434,10 +1438,10 @@ public class Node implements INode, Serializable {
|
|||
propMap.put (p2, prop);
|
||||
}
|
||||
String nID = n.getID();
|
||||
((Transactor) Thread.currentThread ()).visitCleanNode (Key.makeKey (nmap, nID), n);
|
||||
((Transactor) Thread.currentThread ()).visitCleanNode (new Key (nmap, nID), n);
|
||||
// if the field is not the primary key of the property, also register it
|
||||
if (rel != null && !rel.getKeyID(this, p2).equals (nID))
|
||||
((Transactor) Thread.currentThread ()).visitCleanNode (Key.makeKey (rel.other, rel.getKeyID(this, p2)), n);
|
||||
((Transactor) Thread.currentThread ()).visitCleanNode (new Key (rel.other, rel.getKeyID(this, p2)), n);
|
||||
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_ADDED, n));
|
||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.io.*;
|
|||
import java.util.Vector;
|
||||
import java.util.Properties;
|
||||
import Acme.LruHashtable;
|
||||
import helma.util.CacheMap;
|
||||
import helma.objectmodel.*;
|
||||
import helma.framework.core.Application;
|
||||
import com.sleepycat.db.*;
|
||||
|
@ -25,7 +26,7 @@ public final class NodeManager {
|
|||
|
||||
private Application app;
|
||||
|
||||
private LruHashtable cache;
|
||||
private CacheMap cache;
|
||||
|
||||
protected DbWrapper db;
|
||||
|
||||
|
@ -41,7 +42,7 @@ public final class NodeManager {
|
|||
public NodeManager (Application app, String dbHome, Properties props) throws DbException {
|
||||
this.app = app;
|
||||
int cacheSize = Integer.parseInt (props.getProperty ("cachesize", "1000"));
|
||||
cache = new LruHashtable (cacheSize, 0.9f);
|
||||
cache = new CacheMap (cacheSize, 0.8f);
|
||||
IServer.getLogger().log ("set up node cache ("+cacheSize+")");
|
||||
|
||||
safe = new WrappedNodeManager (this);
|
||||
|
@ -112,6 +113,10 @@ public final class NodeManager {
|
|||
}
|
||||
}
|
||||
|
||||
/* private synchronized Node storeNodeInCache (Node n, Key k, Key pk) {
|
||||
|
||||
} */
|
||||
|
||||
public void shutdown () throws DbException {
|
||||
db.shutdown ();
|
||||
this.cache = null;
|
||||
|
@ -134,7 +139,7 @@ public final class NodeManager {
|
|||
if (kstr == null)
|
||||
return null;
|
||||
|
||||
Key key = Key.makeKey (dbmap, kstr);
|
||||
Key key = new Key (dbmap, kstr);
|
||||
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
// tx.timer.beginEvent ("getNode "+kstr);
|
||||
|
@ -153,19 +158,17 @@ public final class NodeManager {
|
|||
|
||||
// The requested node isn't in the shared cache. Synchronize with key to make sure only one
|
||||
// version is fetched from the database.
|
||||
synchronized (key) {
|
||||
|
||||
// check again because only in the synchronized section can we be sure that
|
||||
// another thread hasn't fetched the node in the meantime.
|
||||
node = (Node) cache.get (key);
|
||||
|
||||
if (node == null || node.getState() == Node.INVALID) {
|
||||
node = getNodeByKey (db, tx.txn, kstr, dbmap);
|
||||
if (node != null) {
|
||||
cache.put (node.getKey (), node);
|
||||
node = getNodeByKey (db, tx.txn, kstr, dbmap);
|
||||
if (node != null) {
|
||||
synchronized (cache) {
|
||||
Node oldnode = (Node) cache.put (node.getKey (), node);
|
||||
if (oldnode != null && oldnode.getState () != Node.INVALID) {
|
||||
cache.put (node.getKey (), oldnode);
|
||||
node = oldnode;
|
||||
}
|
||||
}
|
||||
} // synchronized
|
||||
} // synchronized
|
||||
}
|
||||
}
|
||||
|
||||
if (node != null)
|
||||
|
@ -186,7 +189,7 @@ public final class NodeManager {
|
|||
key = home.getKey ().getVirtualKey (kstr);
|
||||
// if a key for a node from within the DB
|
||||
else
|
||||
key = Key.makeKey (rel.other, rel.getKeyID (home, kstr));
|
||||
key = new Key (rel.other, rel.getKeyID (home, kstr));
|
||||
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
// tx.timer.beginEvent ("getNode "+kstr);
|
||||
|
@ -198,41 +201,52 @@ public final class NodeManager {
|
|||
// tx.timer.endEvent ("getNode "+kstr);
|
||||
// if we didn't fetch the node via its primary key, refresh the primary key in the cache.
|
||||
// otherwise we risk cache corroption (duplicate node creation) if the node is fetched by its primary key
|
||||
if (!rel.usesPrimaryKey ())
|
||||
cache.put (node.getKey (), node);
|
||||
if (!rel.usesPrimaryKey ()) {
|
||||
synchronized (cache) {
|
||||
Node oldnode = (Node) cache.put (node.getKey (), node);
|
||||
if (oldnode != null && oldnode.getState () != Node.INVALID) {
|
||||
cache.put (node.getKey (), oldnode);
|
||||
cache.put (key, oldnode);
|
||||
node = oldnode;
|
||||
}
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
// try to get the node from the shared cache
|
||||
node = (Node) cache.get (key);
|
||||
if (node == null || node.getState() == Node.INVALID) {
|
||||
|
||||
if (node == null || node.getState() == Node.INVALID) {
|
||||
// The requested node isn't in the shared cache. Synchronize with key to make sure only one
|
||||
// version is fetched from the database.
|
||||
synchronized (key) {
|
||||
|
||||
// check again because only in the synchronized section can we be sure that
|
||||
// another thread hasn't fetched the node in the meantime.
|
||||
node = (Node) cache.get (key);
|
||||
|
||||
if (node == null || node.getState() == Node.INVALID) {
|
||||
node = getNodeByRelation (db, tx.txn, home, kstr, rel);
|
||||
if (node != null) {
|
||||
Key primKey = node.getKey ();
|
||||
cache.put (primKey, node);
|
||||
if (!key.equals (primKey)) {
|
||||
// cache node with extra (non-primary but unique) key
|
||||
cache.put (key, node);
|
||||
}
|
||||
node = getNodeByRelation (db, tx.txn, home, kstr, rel);
|
||||
if (node != null) {
|
||||
Key primKey = node.getKey ();
|
||||
synchronized (cache) {
|
||||
Node oldnode = (Node) cache.put (primKey, node);
|
||||
if (oldnode != null && oldnode.getState () != Node.INVALID) {
|
||||
cache.put (primKey, oldnode);
|
||||
cache.put (key, oldnode);
|
||||
node = oldnode;
|
||||
}
|
||||
}
|
||||
} // synchronized
|
||||
} // synchronized
|
||||
}
|
||||
}
|
||||
|
||||
if (node != null) {
|
||||
// update primary key in cache, see above
|
||||
if (!rel.usesPrimaryKey ())
|
||||
cache.put (node.getKey (), node);
|
||||
if (!rel.usesPrimaryKey ()) {
|
||||
synchronized (cache) {
|
||||
Node oldnode = (Node) cache.put (node.getKey (), node);
|
||||
if (oldnode != null && oldnode.getState () != Node.INVALID) {
|
||||
cache.put (node.getKey (), oldnode);
|
||||
cache.put (key, oldnode);
|
||||
node = oldnode;
|
||||
}
|
||||
}
|
||||
}
|
||||
tx.visitCleanNode (node.getKey (), node);
|
||||
}
|
||||
|
||||
|
@ -577,9 +591,12 @@ public final class NodeManager {
|
|||
retval.addElement (node.getID());
|
||||
Key primKey = node.getKey ();
|
||||
// do we need to synchronize on primKey here?
|
||||
Node oldnode = (Node) cache.get (primKey);
|
||||
if (oldnode == null || oldnode.getState() == INode.INVALID)
|
||||
cache.put (primKey, node);
|
||||
synchronized (cache) {
|
||||
Node oldnode = (Node) cache.put (primKey, node);
|
||||
if (oldnode != null && oldnode.getState() != INode.INVALID) {
|
||||
cache.put (primKey, oldnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue