From e20aaec618b95497bf70ea1705980d6799b42265 Mon Sep 17 00:00:00 2001 From: hns Date: Thu, 18 Jan 2001 14:49:13 +0000 Subject: [PATCH] Changed Key object management and database fetching strategy. Parallel fetching is now allowed, but at cache insertion strict checks are performed. --- src/helma/objectmodel/Key.java | 29 ++----- src/helma/objectmodel/db/Node.java | 12 ++- src/helma/objectmodel/db/NodeManager.java | 95 +++++++++++++---------- 3 files changed, 72 insertions(+), 64 deletions(-) diff --git a/src/helma/objectmodel/Key.java b/src/helma/objectmodel/Key.java index b04c2354..5ea5f9e3 100644 --- a/src/helma/objectmodel/Key.java +++ b/src/helma/objectmodel/Key.java @@ -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) { diff --git a/src/helma/objectmodel/db/Node.java b/src/helma/objectmodel/db/Node.java index 05c36c71..c0ed8a6b 100644 --- a/src/helma/objectmodel/db/Node.java +++ b/src/helma/objectmodel/db/Node.java @@ -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)); diff --git a/src/helma/objectmodel/db/NodeManager.java b/src/helma/objectmodel/db/NodeManager.java index 45b00e4f..0ebad4ea 100644 --- a/src/helma/objectmodel/db/NodeManager.java +++ b/src/helma/objectmodel/db/NodeManager.java @@ -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 {