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:
hns 2001-01-18 14:49:13 +00:00
parent 8451bfddd9
commit e20aaec618
3 changed files with 72 additions and 64 deletions

View file

@ -4,7 +4,7 @@
package helma.objectmodel; package helma.objectmodel;
import Acme.LruHashtable; import helma.util.CacheMap;
import java.io.Serializable; import java.io.Serializable;
/** /**
@ -13,32 +13,20 @@ import java.io.Serializable;
* key of the node and unique within each HOP application. Currently only * key of the node and unique within each HOP application. Currently only
* single keys are supported. * single keys are supported.
*/ */
public class Key implements Serializable { public final class Key implements Serializable {
protected String type; protected String type;
protected String id; protected String id;
private int hash; private int hash;
private static LruHashtable keycache;
public synchronized static Key makeKey (DbMapping dbmap, String id) { public Key (DbMapping dbmap, String id) {
String _type = dbmap == null ? "" : dbmap.typename; this.type = dbmap == null ? "" : dbmap.typename;
String _id = id.trim (); // removed .toLowerCase() - hw this.id = id;
return makeKey (_type, _id); hash = this.id.hashCode ();
} }
private synchronized static Key makeKey (String _type, String _id) { public Key (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) {
this.type = type; this.type = type;
this.id = id; this.id = id;
hash = this.id.hashCode (); 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. * a key that can't be mistaken for a relational db key.
*/ */
public Key getVirtualKey (String sid) { public Key getVirtualKey (String sid) {
Key virtkey = makeKey ("", getVirtualID (type, id, sid)); return new Key ("", getVirtualID (type, id, sid));
return virtkey;
} }
public static String getVirtualID (DbMapping pmap, String pid, String sid) { public static String getVirtualID (DbMapping pmap, String pid, String sid) {

View file

@ -96,6 +96,7 @@ public class Node implements INode, Serializable {
transient INode cacheNode; transient INode cacheNode;
transient WrappedNodeManager nmgr; transient WrappedNodeManager nmgr;
transient DbMapping dbmap; transient DbMapping dbmap;
transient Key primaryKey = null;
transient DbMapping parentmap; transient DbMapping parentmap;
transient String subnodeRelation = null; transient String subnodeRelation = null;
transient long lastSubnodeFetch = 0; transient long lastSubnodeFetch = 0;
@ -489,6 +490,7 @@ public class Node implements INode, Serializable {
public void setDbMapping (DbMapping dbmap) { public void setDbMapping (DbMapping dbmap) {
if (this.dbmap != dbmap) { if (this.dbmap != dbmap) {
this.dbmap = dbmap; this.dbmap = dbmap;
primaryKey = null;
((Transactor) Thread.currentThread()).visitCleanNode (this); ((Transactor) Thread.currentThread()).visitCleanNode (this);
} }
} }
@ -498,7 +500,9 @@ public class Node implements INode, Serializable {
} }
public Key getKey () { public Key getKey () {
return Key.makeKey (dbmap, id); if (primaryKey == null)
primaryKey = new Key (dbmap, id);
return primaryKey;
} }
public void setSubnodeRelation (String rel) { public void setSubnodeRelation (String rel) {
@ -1249,7 +1253,7 @@ public class Node implements INode, Serializable {
parent.unset (oldvalue); parent.unset (oldvalue);
parent.addNode (this); parent.addNode (this);
// let the node cache know this key's not for this node anymore. // 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); parent.setNode (value, this);
@ -1434,10 +1438,10 @@ public class Node implements INode, Serializable {
propMap.put (p2, prop); propMap.put (p2, prop);
} }
String nID = n.getID(); 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 the field is not the primary key of the property, also register it
if (rel != null && !rel.getKeyID(this, p2).equals (nID)) 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.SUBNODE_ADDED, n));
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));

View file

@ -7,6 +7,7 @@ import java.io.*;
import java.util.Vector; import java.util.Vector;
import java.util.Properties; import java.util.Properties;
import Acme.LruHashtable; import Acme.LruHashtable;
import helma.util.CacheMap;
import helma.objectmodel.*; import helma.objectmodel.*;
import helma.framework.core.Application; import helma.framework.core.Application;
import com.sleepycat.db.*; import com.sleepycat.db.*;
@ -25,7 +26,7 @@ public final class NodeManager {
private Application app; private Application app;
private LruHashtable cache; private CacheMap cache;
protected DbWrapper db; protected DbWrapper db;
@ -41,7 +42,7 @@ public final class NodeManager {
public NodeManager (Application app, String dbHome, Properties props) throws DbException { public NodeManager (Application app, String dbHome, Properties props) throws DbException {
this.app = app; this.app = app;
int cacheSize = Integer.parseInt (props.getProperty ("cachesize", "1000")); 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+")"); IServer.getLogger().log ("set up node cache ("+cacheSize+")");
safe = new WrappedNodeManager (this); 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 { public void shutdown () throws DbException {
db.shutdown (); db.shutdown ();
this.cache = null; this.cache = null;
@ -134,7 +139,7 @@ public final class NodeManager {
if (kstr == null) if (kstr == null)
return null; return null;
Key key = Key.makeKey (dbmap, kstr); Key key = new Key (dbmap, kstr);
Transactor tx = (Transactor) Thread.currentThread (); Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("getNode "+kstr); // 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 // The requested node isn't in the shared cache. Synchronize with key to make sure only one
// version is fetched from the database. // version is fetched from the database.
synchronized (key) {
// check again because only in the synchronized section can we be sure that node = getNodeByKey (db, tx.txn, kstr, dbmap);
// another thread hasn't fetched the node in the meantime. if (node != null) {
node = (Node) cache.get (key); synchronized (cache) {
Node oldnode = (Node) cache.put (node.getKey (), node);
if (node == null || node.getState() == Node.INVALID) { if (oldnode != null && oldnode.getState () != Node.INVALID) {
node = getNodeByKey (db, tx.txn, kstr, dbmap); cache.put (node.getKey (), oldnode);
if (node != null) { node = oldnode;
cache.put (node.getKey (), node);
} }
} } // synchronized
} // synchronized }
} }
if (node != null) if (node != null)
@ -186,7 +189,7 @@ public final class NodeManager {
key = home.getKey ().getVirtualKey (kstr); key = home.getKey ().getVirtualKey (kstr);
// if a key for a node from within the DB // if a key for a node from within the DB
else else
key = Key.makeKey (rel.other, rel.getKeyID (home, kstr)); key = new Key (rel.other, rel.getKeyID (home, kstr));
Transactor tx = (Transactor) Thread.currentThread (); Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("getNode "+kstr); // tx.timer.beginEvent ("getNode "+kstr);
@ -198,41 +201,52 @@ public final class NodeManager {
// tx.timer.endEvent ("getNode "+kstr); // tx.timer.endEvent ("getNode "+kstr);
// if we didn't fetch the node via its primary key, refresh the primary key in the cache. // 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 // otherwise we risk cache corroption (duplicate node creation) if the node is fetched by its primary key
if (!rel.usesPrimaryKey ()) if (!rel.usesPrimaryKey ()) {
cache.put (node.getKey (), node); 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; return node;
} }
// try to get the node from the shared cache // try to get the node from the shared cache
node = (Node) cache.get (key); 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 // The requested node isn't in the shared cache. Synchronize with key to make sure only one
// version is fetched from the database. // version is fetched from the database.
synchronized (key) {
// check again because only in the synchronized section can we be sure that node = getNodeByRelation (db, tx.txn, home, kstr, rel);
// another thread hasn't fetched the node in the meantime. if (node != null) {
node = (Node) cache.get (key); Key primKey = node.getKey ();
synchronized (cache) {
if (node == null || node.getState() == Node.INVALID) { Node oldnode = (Node) cache.put (primKey, node);
node = getNodeByRelation (db, tx.txn, home, kstr, rel); if (oldnode != null && oldnode.getState () != Node.INVALID) {
if (node != null) { cache.put (primKey, oldnode);
Key primKey = node.getKey (); cache.put (key, oldnode);
cache.put (primKey, node); node = oldnode;
if (!key.equals (primKey)) {
// cache node with extra (non-primary but unique) key
cache.put (key, node);
}
} }
} } // synchronized
} // synchronized }
} }
if (node != null) { if (node != null) {
// update primary key in cache, see above // update primary key in cache, see above
if (!rel.usesPrimaryKey ()) if (!rel.usesPrimaryKey ()) {
cache.put (node.getKey (), node); 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); tx.visitCleanNode (node.getKey (), node);
} }
@ -577,9 +591,12 @@ public final class NodeManager {
retval.addElement (node.getID()); retval.addElement (node.getID());
Key primKey = node.getKey (); Key primKey = node.getKey ();
// do we need to synchronize on primKey here? // do we need to synchronize on primKey here?
Node oldnode = (Node) cache.get (primKey); synchronized (cache) {
if (oldnode == null || oldnode.getState() == INode.INVALID) Node oldnode = (Node) cache.put (primKey, node);
cache.put (primKey, node); if (oldnode != null && oldnode.getState() != INode.INVALID) {
cache.put (primKey, oldnode);
}
}
} }
} finally { } finally {