Author: toad
Date: 2008-10-17 22:15:36 +0000 (Fri, 17 Oct 2008)
New Revision: 22997

Added:
   branches/db4o/freenet/src/freenet/support/io/PersistentBlobFreeSlotTag.java
   branches/db4o/freenet/src/freenet/support/io/PersistentBlobTakenSlotTag.java
   branches/db4o/freenet/src/freenet/support/io/PersistentBlobTempBucket.java
   
branches/db4o/freenet/src/freenet/support/io/PersistentBlobTempBucketFactory.java
Modified:
   branches/db4o/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
Log:
Store all temp files of exactly 32KB in a single blob file in the temporary 
directory. Fall back to one-file-per-bucket if something goes wrong. Hopefully 
this will dramatically reduce the number of files in the temp directory and 
therefore prevent OOMing on startup enumerating all the persistent temp files 
(currently this happens in the UOM code, but we will need to enumerate them for 
cleanup soon).


Added: 
branches/db4o/freenet/src/freenet/support/io/PersistentBlobFreeSlotTag.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/io/PersistentBlobFreeSlotTag.java 
                        (rev 0)
+++ branches/db4o/freenet/src/freenet/support/io/PersistentBlobFreeSlotTag.java 
2008-10-17 22:15:36 UTC (rev 22997)
@@ -0,0 +1,11 @@
+package freenet.support.io;
+
+public class PersistentBlobFreeSlotTag {
+       final long index;
+       final PersistentBlobTempBucketFactory factory;
+       
+       PersistentBlobFreeSlotTag(long index, PersistentBlobTempBucketFactory 
factory) {
+               this.index = index;
+               this.factory = factory;
+       }
+}

Added: 
branches/db4o/freenet/src/freenet/support/io/PersistentBlobTakenSlotTag.java
===================================================================
--- 
branches/db4o/freenet/src/freenet/support/io/PersistentBlobTakenSlotTag.java    
                            (rev 0)
+++ 
branches/db4o/freenet/src/freenet/support/io/PersistentBlobTakenSlotTag.java    
    2008-10-17 22:15:36 UTC (rev 22997)
@@ -0,0 +1,13 @@
+package freenet.support.io;
+
+public class PersistentBlobTakenSlotTag {
+       final long index;
+       final PersistentBlobTempBucketFactory factory;
+       final PersistentBlobTempBucket bucket;
+       
+       PersistentBlobTakenSlotTag(long index, PersistentBlobTempBucketFactory 
factory, PersistentBlobTempBucket bucket) {
+               this.index = index;
+               this.factory = factory;
+               this.bucket = bucket;
+       }
+}

Added: 
branches/db4o/freenet/src/freenet/support/io/PersistentBlobTempBucket.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/io/PersistentBlobTempBucket.java  
                        (rev 0)
+++ branches/db4o/freenet/src/freenet/support/io/PersistentBlobTempBucket.java  
2008-10-17 22:15:36 UTC (rev 22997)
@@ -0,0 +1,213 @@
+package freenet.support.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+import com.db4o.ObjectContainer;
+
+import freenet.support.api.Bucket;
+
+/**
+ * A persistent temp bucket stored as a blob in a 
PersistentBlobTempBucketFactory.
+ * @author Matthew Toseland <toad at amphibian.dyndns.org> (0xE43DA450)
+ */
+public class PersistentBlobTempBucket implements Bucket {
+       
+       public final long blockSize;
+       private long size;
+       public final PersistentBlobTempBucketFactory factory;
+       /** The index into the blob file of this specific bucket */
+       final long index;
+       private boolean freed;
+       private boolean readOnly;
+       /** Has this bucket been persisted? If not, it will be only on the 
temporary
+        * map in the factory. */
+       private boolean persisted;
+       private final int hashCode;
+       
+       public int hashCode() {
+               return hashCode;
+       }
+
+       public PersistentBlobTempBucket(PersistentBlobTempBucketFactory 
factory2, long blockSize2, long slot) {
+               factory = factory2;
+               blockSize = blockSize2;
+               index = slot;
+               hashCode = super.hashCode();
+       }
+
+       public Bucket createShadow() throws IOException {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       public void free() {
+               factory.freeBucket(index, this); // will call onFree(): always 
take the outer lock first.
+       }
+       
+       public boolean freed() {
+               return freed;
+       }
+       
+       synchronized void onFree() {
+               freed = true;
+       }
+
+       boolean persisted() {
+               return persisted;
+       }
+       
+       public InputStream getInputStream() throws IOException {
+               if(freed) throw new IOException("Already freed");
+               final FileChannel channel = factory.channel;
+               return new InputStream() {
+
+                       private int offset;
+                       
+                       @Override
+                       public int read() throws IOException {
+                               byte[] buf = new byte[1];
+                               int res = read(buf);
+                               if(res == -1) return -1;
+                               return buf[0];
+                       }
+                       
+                       @Override
+                       public int read(byte[] buffer, int bufOffset, int 
length) throws IOException {
+                               long max;
+                               synchronized(PersistentBlobTempBucket.this) {
+                                       if(freed) throw new IOException("Bucket 
freed during read");
+                                       max = Math.max(blockSize, size);
+                               }
+                               if(offset < 0) return -1; // throw new 
EOFException() ???
+                               if(offset + length >= max)
+                                       length = (int) Math.min(max - offset, 
Integer.MAX_VALUE);
+                               ByteBuffer buf = ByteBuffer.wrap(buffer, 
bufOffset, length);
+                               int read = channel.read(buf, blockSize * index 
+ offset);
+                               if(read > 0) offset += read;
+                               return read;
+                       }
+                       
+                       @Override
+                       public int read(byte[] buffer) throws IOException {
+                               return read(buffer, 0, buffer.length);
+                       }
+                       
+                       public int available() {
+                               return (int) Math.min(blockSize - offset, 
Integer.MAX_VALUE);
+                       }
+                       
+                       public void close() {
+                               // Do nothing.
+                       }
+                       
+               };
+       }
+
+       public String getName() {
+               return factory.getName()+":"+index;
+       }
+
+       public OutputStream getOutputStream() throws IOException {
+               if(freed) throw new IOException("Already freed");
+               if(readOnly) throw new IOException("Read-only");
+               final FileChannel channel = factory.channel;
+               
+               return new OutputStream() {
+
+                       private int offset;
+                       
+                       @Override
+                       public void write(int arg) throws IOException {
+                               byte[] buf = new byte[] { (byte) arg };
+                               write(buf, 0, 1);
+                       }
+                       
+                       @Override
+                       public void write(byte[] buffer, int bufOffset, int 
length) throws IOException {
+                               synchronized(PersistentBlobTempBucket.this) {
+                                       if(freed) throw new IOException("Bucket 
freed during write");
+                                       if(readOnly) throw new 
IOException("Bucket made read only during write");
+                               }
+                               long remaining = blockSize - offset;
+                               if(remaining <= 0) throw new IOException("Too 
big");
+                               if(length > remaining) length = (int) remaining;
+                               ByteBuffer buf = ByteBuffer.wrap(buffer, 
bufOffset, length);
+                               int written = 0;
+                               while(written < length) {
+                                       int w = channel.write(buf, blockSize * 
index + offset);
+                                       offset += w;
+                                       
synchronized(PersistentBlobTempBucket.this) {
+                                               size += w;
+                                       }
+                                       written += w;
+                               }
+                       }
+                       
+                       @Override
+                       public void write(byte[] buffer) throws IOException {
+                               write(buffer, 0, buffer.length);
+                       }
+                       
+               };
+       }
+
+       public synchronized boolean isReadOnly() {
+               return readOnly;
+       }
+
+       public synchronized void setReadOnly() {
+               readOnly = true;
+       }
+
+       public synchronized long size() {
+               return size;
+       }
+
+       // When created, we take up a slot in the temporary (in-RAM) map on the 
factory.
+       // When storeTo() is called the first time, we are committed to a 
persistent 
+       // structure. When removeFrom() is called afterwards, we are moved back 
to the
+       // temporary map, unless we have been freed.
+       
+       public void storeTo(ObjectContainer container) {
+               boolean p;
+               // Race conditions with storeTo and removeFrom running on 
different threads
+               // in parallel are possible... that sort of behaviour *should* 
be very rare,
+               // you should always store it before making it publicly 
available...
+               synchronized(this) {
+                       p = persisted;
+               }
+               if(!p)
+                       factory.store(this, container); // Calls onStore(). 
Take the outer lock first.
+       }
+       
+       public boolean objectCanNew(ObjectContainer container) {
+               synchronized(this) {
+                       if(persisted) return true;
+               }
+               storeTo(container);
+               return true;
+       }
+       
+       synchronized void onStore() {
+               persisted = true;
+       }
+       
+       public void removeFrom(ObjectContainer container) {
+               boolean p;
+               synchronized(this) {
+                       p = persisted;
+               }
+               if(p)
+                       factory.remove(this, container); // Calls onRemove().
+               container.delete(this);
+       }
+       
+       synchronized void onRemove() {
+               persisted = false;
+       }
+
+}

Added: 
branches/db4o/freenet/src/freenet/support/io/PersistentBlobTempBucketFactory.java
===================================================================
--- 
branches/db4o/freenet/src/freenet/support/io/PersistentBlobTempBucketFactory.java
                           (rev 0)
+++ 
branches/db4o/freenet/src/freenet/support/io/PersistentBlobTempBucketFactory.java
   2008-10-17 22:15:36 UTC (rev 22997)
@@ -0,0 +1,288 @@
+package freenet.support.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.TreeSet;
+
+import com.db4o.ObjectContainer;
+import com.db4o.ObjectSet;
+import com.db4o.query.Query;
+
+import freenet.client.async.ClientContext;
+import freenet.client.async.DBJob;
+import freenet.client.async.DBJobRunner;
+import freenet.support.Logger;
+
+/**
+ * Simple temporary storage mechanism using a single file (or a small number 
of 
+ * files), and storing buckets of precisely the block size only. Buckets may 
not
+ * exceed the block size; the rest of the node should only call us if the 
bucket
+ * is of the correct maximum size, and should fall back to one-file-per-bucket
+ * otherwise.
+ * 
+ * Currently we only use one blob file. This means that on FAT and some other
+ * filesystems, the node will have to fall back once we reach 2GB of temp 
files.
+ * @author Matthew Toseland <toad at amphibian.dyndns.org> (0xE43DA450)
+ */
+public class PersistentBlobTempBucketFactory {
+       
+       public final long blockSize;
+       private File storageFile;
+       private transient RandomAccessFile raf;
+       /** We use NIO for the equivalent of pwrite/pread. This is parallelized 
on unix
+        * but sadly not on Windows. */
+       transient FileChannel channel;
+       
+       /** Blobs in memory only: in the database there will still be a "free" 
tag */
+       private transient Map<Long,PersistentBlobTempBucket> notCommittedBlobs;
+       
+       /** Non-exhaustive list of free slots. If we run out we need to query 
for 
+        * more. */
+       private transient TreeSet<Long> freeSlots;
+       
+       private transient DBJobRunner jobRunner;
+       
+       private transient Random weakRandomSource;
+       
+       private final long nodeDBHandle;
+       
+       public PersistentBlobTempBucketFactory(long blockSize2, long 
nodeDBHandle2, File storageFile2) {
+               blockSize = blockSize2;
+               nodeDBHandle = nodeDBHandle2;
+               storageFile = storageFile2;
+       }
+
+       void onInit(ObjectContainer container, DBJobRunner jobRunner2, Random 
fastWeakRandom, File storageFile2, long blockSize2) throws IOException {
+               container.activate(storageFile, 100);
+               if(storageFile2.getPath().equals(storageFile.getPath())) {
+                       if(blockSize != blockSize2)
+                               throw new IllegalStateException("My block size 
is "+blockSize2+
+                                               " but stored block size is 
"+blockSize+
+                                               " for same file "+storageFile);
+               } else {
+                       if(!FileUtil.moveTo(storageFile, storageFile2, false))
+                               throw new IOException("Unable to move temp blob 
file from "+storageFile+" to "+storageFile2);
+               }
+               raf = new RandomAccessFile(storageFile, "rw");
+               channel = raf.getChannel();
+               notCommittedBlobs = new 
HashMap<Long,PersistentBlobTempBucket>();
+               freeSlots = new TreeSet<Long>();
+               jobRunner = jobRunner2;
+               weakRandomSource = fastWeakRandom;
+       }
+
+       public String getName() {
+               return storageFile.getPath();
+       }
+       
+       private final DBJob slotFinder = new DBJob() {
+               
+               public void run(ObjectContainer container, ClientContext 
context) {
+                       boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
+                       synchronized(PersistentBlobTempBucketFactory.this) {
+                               if(freeSlots.size() > 1024) return;
+                       }
+                       Query query = container.query();
+                       
query.constrain(PersistentBlobFreeSlotTag.class).and(query.descend("factory").constrain(PersistentBlobTempBucketFactory.this));
+                       ObjectSet<PersistentBlobFreeSlotTag> tags = 
query.execute();
+                       Long[] notCommitted;
+                       int added = 0;
+                       synchronized(PersistentBlobTempBucketFactory.this) {
+                               while(tags.hasNext()) {
+                                       PersistentBlobFreeSlotTag tag = 
tags.next();
+                                       if(notCommittedBlobs.get(tag.index) != 
null) continue;
+                                       if(freeSlots.contains(tag.index)) 
continue;
+                                       Query check = container.query();
+                                       
check.constrain(PersistentBlobTakenSlotTag.class);
+                                       
check.descend("index").constrain(tag.index).and(check.descend("factory").constrain(PersistentBlobTempBucketFactory.this));
+                                       ObjectSet<PersistentBlobTakenSlotTag> 
checkResults = check.execute();
+                                       if(checkResults.hasNext()) {
+                                               Logger.error(this, "slot 
"+tag.index+" is already taken by "+checkResults.next().bucket+", but also has 
a FreeSlotTag! total matches: "+(checkResults.size()+1));
+                                               container.delete(tag);
+                                               continue;
+                                       }
+                                       if(logMINOR) Logger.minor(this, "Adding 
slot "+tag.index+" to freeSlots (has a free tag and no taken tag)");
+                                       freeSlots.add(tag.index);
+                                       added++;
+                                       if(added > 1024) return;
+                               }
+                       }
+                       long size;
+                       try {
+                               size = channel.size();
+                       } catch (IOException e1) {
+                               Logger.error(this, "Unable to find size of temp 
blob storage file: "+e1, e1);
+                               return;
+                       }
+                       size -= size % blockSize;
+                       long blocks = size / blockSize;
+                       long ptr = blocks - 1;
+                       query = container.query();
+                       query.constrain(PersistentBlobTakenSlotTag.class);
+                       
query.descend("index").constrain(ptr).and(query.descend("factory").constrain(PersistentBlobTempBucketFactory.this));
+                       // Try from the end, until we find no more.
+                       while(ptr > 0 && !query.execute().hasNext()) {
+                               boolean stored = false;
+                               
synchronized(PersistentBlobTempBucketFactory.this) {
+                                       stored = notCommittedBlobs.get(ptr) == 
null;
+                                       if(stored) {
+                                               if(freeSlots.contains(ptr)) 
break;
+                                               freeSlots.add(ptr);
+                                       }
+                               }
+                               query = container.query();
+                               
query.constrain(PersistentBlobFreeSlotTag.class);
+                               
query.descend("index").constrain(ptr).and(query.descend("factory").constrain(PersistentBlobTempBucketFactory.this));
+                               if(query.execute().hasNext()) break;
+                               if(stored) {
+                                       container.store(new 
PersistentBlobFreeSlotTag(ptr, PersistentBlobTempBucketFactory.this));
+                                       added++;
+                               }
+                               if(logMINOR)
+                                       Logger.minor(this, "Adding slot "+ptr+" 
to freeSlots, searching for free slots from the end");
+                               if(added > 1024) return;
+                               ptr--;
+                               query = container.query();
+                               
query.constrain(PersistentBlobTakenSlotTag.class);
+                               
query.descend("index").constrain(ptr).and(query.descend("factory").constrain(PersistentBlobTempBucketFactory.this));
+                       }
+                       // We haven't done an exhaustive search for freeable 
slots, slots
+                       // with no tag at all etc. This happens on startup.
+                       // Lets extend the file.
+                       // FIXME if physical security is LOW, just set the 
length, possibly
+                       // padding will nonrandom nulls on unix.
+                       long extendBy = blockSize * 32;
+                       long written = 0;
+                       byte[] buf = new byte[4096];
+                       ByteBuffer buffer = ByteBuffer.wrap(buf);
+                       while(written < extendBy) {
+                               weakRandomSource.nextBytes(buf);
+                               int bytesLeft = (int) Math.min(extendBy - 
written, Integer.MAX_VALUE);
+                               if(bytesLeft < buf.length)
+                                       buffer.limit(bytesLeft);
+                               try {
+                                       written += channel.write(buffer, size + 
written);
+                                       buffer.clear();
+                               } catch (IOException e) {
+                                       break;
+                               }
+                       }
+                       for(int i=0;i<written / blockSize;i++) {
+                               ptr = blocks + i;
+                               container.store(new 
PersistentBlobFreeSlotTag(ptr, PersistentBlobTempBucketFactory.this));
+                               
synchronized(PersistentBlobTempBucketFactory.this) {
+                                       freeSlots.add(ptr);
+                                       if(logMINOR)
+                                               Logger.minor(this, "Adding slot 
"+ptr+" to freeSlots while extending storage file");
+                               }
+                       }
+               }
+               
+       };
+       
+       /**
+        * @return A bucket, or null in various failure cases.
+        */
+       public PersistentBlobTempBucket makeBucket() {
+               // Find a free slot.
+               long slotNo;
+               synchronized(this) {
+                       if(!freeSlots.isEmpty()) {
+                               Long slot = freeSlots.first();
+                               freeSlots.remove(slot);
+                               if(notCommittedBlobs.get(slot) != null) {
+                                       Logger.error(this, "Slot "+slot+" 
already occupied despite being in freeSlots!!");
+                                       return null;
+                               }
+                               PersistentBlobTempBucket bucket = new 
PersistentBlobTempBucket(this, blockSize, slot);
+                               notCommittedBlobs.put(slot, bucket);
+                               if(Logger.shouldLog(Logger.MINOR, this)) 
Logger.minor(this, "Using slot "+slot+" for "+bucket);
+                               return bucket;
+                       }
+               }
+               jobRunner.runBlocking(slotFinder, NativeThread.HIGH_PRIORITY);
+               synchronized(this) {
+                       if(!freeSlots.isEmpty()) {
+                               Long slot = freeSlots.first();
+                               freeSlots.remove(slot);
+                               if(notCommittedBlobs.get(slot) != null) {
+                                       Logger.error(this, "Slot "+slot+" 
already occupied despite being in freeSlots!!");
+                                       return null;
+                               }
+                               PersistentBlobTempBucket bucket = new 
PersistentBlobTempBucket(this, blockSize, slot);
+                               notCommittedBlobs.put(slot, bucket);
+                               if(Logger.shouldLog(Logger.MINOR, this)) 
Logger.minor(this, "Using slot "+slot+" for "+bucket+" (2)");
+                               return bucket;
+                       }
+               }
+               Logger.error(this, "Returning null, unable to create a bucket 
for some reason, node will fallback to file-based buckets");
+               return null;
+       }
+
+       public synchronized void freeBucket(long index, 
PersistentBlobTempBucket bucket) {
+               notCommittedBlobs.remove(index);
+               bucket.onFree();
+       }
+
+       public synchronized void remove(PersistentBlobTempBucket bucket, 
ObjectContainer container) {
+               if(Logger.shouldLog(Logger.MINOR, this))
+                       Logger.minor(this, "Removing bucket "+bucket+" for slot 
"+bucket.index+" from database");
+               if(!bucket.persisted()) return;
+               Query query = container.query();
+               long index = bucket.index;
+               freeSlots.add(index);
+               query.constrain(PersistentBlobTakenSlotTag.class);
+               
query.descend("index").constrain(index).and(query.descend("factory").constrain(PersistentBlobTempBucketFactory.this));
+               ObjectSet<PersistentBlobTakenSlotTag> tags = query.execute();
+               while(tags.hasNext()) {
+                       PersistentBlobTakenSlotTag tag = tags.next();
+                       if(Logger.shouldLog(Logger.MINOR, this))
+                               Logger.minor(this, "Deleting taken slot tag for 
index "+index+" : "+tag+" : "+tag.index);
+                       container.delete(tag);
+               }
+               query = container.query();
+               
query.constrain(PersistentBlobFreeSlotTag.class).and(query.descend("index").constrain(index).and(query.descend("factory").constrain(PersistentBlobTempBucketFactory.this)));
+               if(!query.execute().hasNext()) {
+                       PersistentBlobFreeSlotTag tag = new 
PersistentBlobFreeSlotTag(index, this);
+                       container.store(tag);
+               }
+               bucket.onRemove();
+       }
+
+       public void store(PersistentBlobTempBucket bucket, ObjectContainer 
container) {
+               if(Logger.shouldLog(Logger.MINOR, this))
+                       Logger.minor(this, "Storing bucket "+bucket+" for slot 
"+bucket.index+" to database");
+               long index = bucket.index;
+               // FIXME paranoid check, remove
+               Query query = container.query();
+               query.constrain(PersistentBlobTakenSlotTag.class);
+               
query.descend("index").constrain(index).and(query.descend("factory").constrain(PersistentBlobTempBucketFactory.this));
+               ObjectSet<PersistentBlobTakenSlotTag> taken = query.execute();
+               if(taken.hasNext()) {
+                       PersistentBlobTakenSlotTag tag = taken.next();
+                       Logger.error(this, "Slot "+index+" already occupied!: 
"+tag.bucket+" for "+tag.index);
+                       throw new IllegalStateException("Slot "+index+" already 
occupied!");
+               }
+               // Now the normal bit
+               query = container.query();
+               query.constrain(PersistentBlobFreeSlotTag.class);
+               
query.descend("index").constrain(index).and(query.descend("factory").constrain(PersistentBlobTempBucketFactory.this));
+               ObjectSet<PersistentBlobFreeSlotTag> results = query.execute();
+               while(results.hasNext()) {
+                       PersistentBlobFreeSlotTag tag = results.next();
+                       if(Logger.shouldLog(Logger.MINOR, this))
+                               Logger.minor(this, "Deleting free slot tag for 
index "+index+" : "+tag+" : "+tag.index);
+                       container.delete(tag);
+               }
+               bucket.onStore();
+               PersistentBlobTakenSlotTag tag = new 
PersistentBlobTakenSlotTag(index, this, bucket);
+               container.store(tag);
+       }
+
+}

Modified: 
branches/db4o/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
===================================================================
--- 
branches/db4o/freenet/src/freenet/support/io/PersistentTempBucketFactory.java   
    2008-10-17 22:09:23 UTC (rev 22996)
+++ 
branches/db4o/freenet/src/freenet/support/io/PersistentTempBucketFactory.java   
    2008-10-17 22:15:36 UTC (rev 22997)
@@ -14,7 +14,9 @@
 import com.db4o.ObjectSet;
 import com.db4o.query.Predicate;

+import freenet.client.async.DBJobRunner;
 import freenet.crypt.RandomSource;
+import freenet.keys.CHKBlock;
 import freenet.support.Logger;
 import freenet.support.api.Bucket;
 import freenet.support.api.BucketFactory;
@@ -46,9 +48,14 @@
        private final long nodeDBHandle;

        private volatile boolean encrypt;
+       
+       private final PersistentBlobTempBucketFactory blobFactory;
+       
+       static final int BLOB_SIZE = CHKBlock.DATA_LENGTH;

        public PersistentTempBucketFactory(File dir, final String prefix, 
RandomSource strongPRNG, Random weakPRNG, boolean encrypt, long nodeDBHandle) 
throws IOException {
                boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
+               blobFactory = new PersistentBlobTempBucketFactory(BLOB_SIZE, 
nodeDBHandle, new File(dir, "persistent-blob.tmp"));
                this.strongPRNG = strongPRNG;
                this.nodeDBHandle = nodeDBHandle;
                this.weakPRNG = weakPRNG;
@@ -121,7 +128,12 @@
        }

        public Bucket makeBucket(long size) throws IOException {
-               PersistentTempFileBucket rawBucket = new 
PersistentTempFileBucket(fg.makeRandomFilename(), fg);
+               Bucket rawBucket = null;
+               if(size == BLOB_SIZE) {
+                       rawBucket = blobFactory.makeBucket();
+               }
+               if(rawBucket == null)
+                       rawBucket = new 
PersistentTempFileBucket(fg.makeRandomFilename(), fg);
                Bucket maybeEncryptedBucket = (encrypt ? new 
PaddedEphemerallyEncryptedBucket(rawBucket, 1024, strongPRNG, weakPRNG) : 
rawBucket);
                return new DelayedFreeBucket(this, maybeEncryptedBucket);
        }
@@ -163,7 +175,7 @@
                return encrypt;
        }

-       public static PersistentTempBucketFactory load(File dir, String prefix, 
RandomSource random, Random fastWeakRandom, ObjectContainer container, final 
long nodeDBHandle, boolean encrypt) throws IOException {
+       public static PersistentTempBucketFactory load(File dir, String prefix, 
RandomSource random, Random fastWeakRandom, ObjectContainer container, final 
long nodeDBHandle, boolean encrypt, DBJobRunner jobRunner) throws IOException {
                ObjectSet<PersistentTempBucketFactory> results = 
container.query(new Predicate<PersistentTempBucketFactory>() {
                        public boolean match(PersistentTempBucketFactory 
factory) {
                                if(factory.nodeDBHandle == nodeDBHandle) return 
true;
@@ -175,9 +187,14 @@
                        container.activate(factory, 5);
                        factory.init(dir, prefix, random, fastWeakRandom);
                        factory.setEncryption(encrypt);
+                       factory.blobFactory.onInit(container, jobRunner, 
fastWeakRandom, new File(dir, "persistent-blob.tmp"), BLOB_SIZE);
                        return factory;
-               } else
-                       return new PersistentTempBucketFactory(dir, prefix, 
random, fastWeakRandom, encrypt, nodeDBHandle);
+               } else {
+                       PersistentTempBucketFactory factory =
+                               new PersistentTempBucketFactory(dir, prefix, 
random, fastWeakRandom, encrypt, nodeDBHandle);
+                       factory.blobFactory.onInit(container, jobRunner, 
fastWeakRandom, new File(dir, "persistent-blob.tmp"), BLOB_SIZE);
+                       return factory;
+               }
        }

        public void setEncryption(boolean encrypt) {


Reply via email to