diff src/org/dancres/blitz/entry/WriteScheduler.java @ 0:3dc0c5604566

Initial checkin of blitz 2.0 fcs - no installer yet.
author Dan Creswell <dan.creswell@gmail.com>
date Sat, 21 Mar 2009 11:00:06 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/dancres/blitz/entry/WriteScheduler.java	Sat Mar 21 11:00:06 2009 +0000
@@ -0,0 +1,122 @@
+package org.dancres.blitz.entry;
+
+import java.io.IOException;
+
+import java.util.HashMap;
+
+import java.util.logging.Level;
+
+import org.dancres.blitz.oid.OID;
+import org.dancres.blitz.entry.ci.CacheIndexer;
+
+/**
+   <p> Transactional writes add a further complication to the caching process
+   because, whilst they can be flushed from SleeveCache, they can't necessarily
+   be written to disk.  Consider the case where a transaction starts and
+   writes an entry, other activity causes that entry to be flushed from
+   SleeveCache into the asynchronous write queue and then saved to disk.
+   The system now crashes prior to the original writing transaction issuing
+   a successful prepare.  At recovery, we will have an additional entry on
+   disk unprotected by transactional locks and it would be valid for matching.
+   </p>
+
+   <p> WriteScheduler helps solve this problem by supporting "Transient"
+   EntrySleeveImpls.  EntrySleeveImpls marked as transient are held in
+   WriteScheduler but not schedule to be written to disk.  WriteScheduler
+   maintains an index of all Entry's to assist in searching so these
+   Transient's are available for matching.  When a transaction commits, the
+   Transient mark is removed from EntrySleeveImpls (these sleeves must be
+   reloaded into SleeveCache for this to happen) and they are re-submitted to
+   WriteScheduler at which point they are scheduled for writing. </p>
+
+   <p> Thus, until a transaction is commited/aborted, any EntrySleeveImpls
+   written are marked Transient.  During commit/abort, these Sleeves have the
+   mark removed and are thus eligible for writing to disk. </p>
+*/
+class WriteScheduler {
+    private HashMap theTransients = new HashMap();
+
+    private WriteBuffer theBuffer;
+	private String theType;
+	
+    WriteScheduler(EntryEditor anEditor) {
+		theType = anEditor.getType();
+        theBuffer = new WriteBuffer(anEditor);
+    }
+
+    public int getSize() {
+        throw new org.dancres.util.NotImplementedException();
+    }
+
+    void add(EntrySleeveImpl aSleeve) throws IOException {
+        /*
+            Cache has no concept of dirty, it flushes content out of a block
+            whenever it wishes to use the block for something else.  So,
+            it's possible we'll be asked to flush an empty block.
+         */
+        if (aSleeve == null)
+            return;
+
+        /*
+            Whether the sleeve is dirty or not, if it's pinned we remember it.
+            Pinned implies some entity will do more with this sleeve later
+            including potentially dirtying state which might be clean at
+            this stage.  Thus we must remember the state clean or dirty until
+            the pin is released.
+         */
+        SleeveState myState = aSleeve.getState();
+
+        if (myState.test(SleeveState.PINNED)) {
+            synchronized(theTransients) {
+                theTransients.put(aSleeve.getOID(), aSleeve);
+            }
+
+            return;
+        }
+
+        /*
+          Cache flushed the entry - do we need to save it?
+        */
+        if (!aSleeve.isDirty())
+            return;
+
+        boolean dontWrite = myState.test(SleeveState.NOT_ON_DISK) &&
+            myState.test(SleeveState.DELETED);
+
+        /*
+            If we're not writing this entry, clear it from the indexer now
+            otherwise leave it to the write buffer to do when it's pushed
+            the entry to disk
+        if (dontWrite) {
+            CacheIndexer.getIndexer(theType).flushed(aSleeve);
+        }
+         */
+
+        synchronized(theTransients) {
+            theTransients.remove(aSleeve.getOID());
+
+            if (!dontWrite)
+                theBuffer.add(aSleeve);
+        }
+
+        /*
+         Note we do not manage NOT_ON_DISK - this is dealt with in the
+         WriteBuffer as it's vital state it needs to determine whether or
+         not to actually update disk.
+        */
+        aSleeve.clearDirty();
+    }
+
+    EntrySleeveImpl dirtyRead(OID aUID) {
+        EntrySleeveImpl mySleeve = null;
+
+        synchronized(theTransients) {
+            mySleeve = (EntrySleeveImpl) theTransients.get(aUID);
+        }
+
+        if (mySleeve == null)
+            mySleeve = theBuffer.dirtyRead(aUID);
+
+        return mySleeve;
+    }
+}