* Matthew Toseland <toad at amphibian.dyndns.org> [2008-08-25 22:15:24]:
> On Monday 25 August 2008 14:46, nextgens at freenetproject.org wrote:
> > Author: nextgens
> > Date: 2008-08-25 13:46:46 +0000 (Mon, 25 Aug 2008)
> > New Revision: 22133
> >
> > Modified:
> > trunk/freenet/src/freenet/node/NodeClientCore.java
> > trunk/freenet/src/freenet/support/io/ArrayBucket.java
> > trunk/freenet/src/freenet/support/io/ArrayBucketFactory.java
> >
> trunk/freenet/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
> > trunk/freenet/src/freenet/support/io/TempBucketFactory.java
> > trunk/freenet/test/freenet/clients/http/filter/ContentFilterTest.java
> > trunk/freenet/test/freenet/support/compress/GzipCompressorTest.java
> > Log:
> > TempBucketFactory: it is *very* unlikely that it won't break things! If it
> doesn't it should speed things up significantly and solve the OOM problems a
> few users have been reporting.
> >
> > Why isn't PaddedEphemerallyEncryptedBucket not properly synchronized?
> >
> >
> > - // Override this or FOS will use write(int)
> > - public void write(byte[] buf) throws IOException {
> > - if(closed) throw new IOException("Already closed!");
> > - if(streamNumber != lastOutputStream)
> > - throw new IllegalStateException("Writing to old
> > stream in "+getName());
> > - write(buf, 0, buf.length);
> > - }
> > -
>
> Please don't delete this method. It is necessary for acceptable performance.
> The default implementation will call write() repeatedly on single bytes,
> *not* write(buf, 0, length).
>
reverted in r22150
> > + /** How big can the defaultSize be for us to consider using RAMBuckets?
> > */
> > + private long maxRAMBucketSize;
> > + /** How much memory do we dedicate to the RAMBucketPool? (in bytes) */
> > + private long maxRamUsed;
> > +
> > + /** How old is a long-lived RAMBucket? */
> > + private final int RAMBUCKET_MAX_AGE = 5*60*1000; // 5mins
> > + /** How many times the maxRAMBucketSize can a RAMBucket be before it
> > gets
> migrated? */
> > + private final int RAMBUCKET_CONVERSION_FACTOR = 4;
>
> I'm not sure what the point of this is - why not just use maxRAMBucketSize ??
Because most calls to createBucket() will pass a decent estimate of
their size requirements.
> */
> > + private final void migrateToFileBucket() throws IOException {
> > + Bucket toMigrate = null;
> > + synchronized(currentBucket) {
>
> You shouldn't synchronize on it if it is going to change. Can't you use
> synchronized(this)?
We need a shared lock on the Streams too... but yes I could do it
>
> > if(!isRAMBucket())
> > + // Nothing to migrate! We don't want to
> > switch back to ram, do we?
> > return;
> >
> > - ramBucket = (RAMBucket) currentBucket;
> > - TempFileBucket tempFB = new
> TempFileBucket(filenameGenerator.makeRandomFilename(), filenameGenerator);
> > + toMigrate = currentBucket;
> > + Bucket tempFB = _makeFileBucket();
> > BucketTools.copy(currentBucket, tempFB);
>
> If an OutputStream is open, you need to close it, open one to the new bucket,
> copy the old bucket to the new stream, and *keep the new stream because
> buckets don't support appending*. Discussed on IRC.
>
Ok, fixed in r22149
> > currentBucket = tempFB;
> > + // We need streams to be reset to point to the
> > new bucket
> > + shouldResetOS = true;
> > + shouldResetIS = true;
> > }
> > - ramBucket.free();
> > + if(logMINOR)
> > + Logger.minor(this, "We have migrated
> > "+toMigrate.hashCode());
> > +
> > + // Might have changed already so we can't rely on
> > currentSize!
> > + _hasFreed(toMigrate.size());
> > + // We can free it on-thread as it's a rambucket
> > + toMigrate.free();
>
> We should update the counter *after* freeing the RAM bucket.
Done in r22151
> > + private void _maybeMigrateRamBucket(long futureSize)
> > throws IOException
> {
> > + if(isRAMBucket()) {
> > + boolean shouldMigrate = false;
> > + boolean isOversized = false;
> > +
> > + if(futureSize > maxRAMBucketSize *
> > RAMBUCKET_CONVERSION_FACTOR) {
> > + isOversized = true;
> > + shouldMigrate = true;
> > + } else if (futureSize + currentSize >
> > maxRamUsed)
>
> What is futureSize + currentSize supposed to mean? Don't you mean bytesInUse?
>
I meant futureSize - currentSize + bytesInUse ... fixed in r22153
> > +
> > + @Override
> > + public void flush() throws IOException {
> > + synchronized(currentBucket) {
> > + _maybeMigrateRamBucket(currentSize);
> > + _maybeResetOutputStream();
> > + os.flush();
> > + }
> > + }
>
> No point flushing *after* you've reset the OS, is there? Migrating will
> close() and therefore flush. Of course if you don't, you do want to call
> flush() here.
If there is a BufferedOuputStream involved on the underlying buckets it
will clobber any IOException thrown on close() hence flushing is a good
idea.
> If the implementation changes, you need to seek to where the stream was
> before
> it changed. If the implementation stays the same and a new output stream is
> opened, the input stream should throw. The easiest way to do this is to
> increment a counter on every getOutputStream(), and have each input and
> output stream know the counter at the time of its creation, and throw if it
> changes in read/write.
I'll implement that soon
> > +
> > + @Override
> > + public int read() throws IOException {
> > + synchronized(currentBucket) {
> > + _maybeResetInputStream();
> > + return is.read();
> > + }
> > + }
>
> How come no bulk read? Always reading 1 byte at a time will be really slow.
Done in r22154
> > + if(toMigrate.size() > 0) {
> > + executor.execute(new Runnable() {
> > +
> > + public void run() {
> > + if(logMINOR)
> > + Logger.minor(this, "We are
> > going to migrate " + toMigrate.size() + "
> RAMBuckets");
> > + for(TempBucket tmpBucket : toMigrate) {
> > + try {
> > +
> > tmpBucket.migrateToFileBucket();
> > + } catch(IOException e) {
> > + Logger.error(tmpBucket,
> > "An IOE occured while migrating long-lived
> buckets:" + e.getMessage(), e);
> > + }
> > + }
> > + }
> > + }, "RAMBucket migrator ("+now+')');
> > + }
> > }
>
> This is always offline; should it be done inline if we immediately need the
> space for a new bucket? Making such a bucket would require disk access in any
> case... Also, if it is happening inline, you can migrate the oldest bucket
> even if it's not that old, subject to some reasonable minimum age (if the
> oldest bucket is less than 1 minute old, don't migrate it, just create a
> disk-based bucket).
I'll think about it; let's see how the fixed code behaves first.
Thanks for reviewing it anyway :)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 197 bytes
Desc: Digital signature
URL:
<https://emu.freenetproject.org/pipermail/devl/attachments/20080825/fda42843/attachment.pgp>