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;
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) {

View file

@ -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));

View file

@ -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,20 +158,18 @@ 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);
}
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
}
}
if (node != null)
tx.visitCleanNode (node.getKey (), node);
@ -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);
}
}
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
}
}
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 {