dain / leveldb

Port of LevelDB to Java

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

DB.put fails with java.lang.IllegalArgumentException: key must be greater than last key

vladt opened this issue · comments

The following code:
package testleveldb;

import java.io.File;
import java.util.Random;

import org.iq80.leveldb.CompressionType;
import org.iq80.leveldb.DB;
import org.iq80.leveldb.Options;
import org.iq80.leveldb.impl.Iq80DBFactory;

public class Test
{
private static final byte[] NULL = new byte[0];

public static void main( String[] args )
    throws Exception
{
    Options options = new Options();
    options.createIfMissing( true );
    options.compressionType( CompressionType.SNAPPY );
    options.writeBufferSize( 16 * 1024 * 1024 );
    DB db = new Iq80DBFactory().open( new File( "/var/tmp/test/testleveldb" ), options );

    int progress = 0;
    byte[] key = new byte[40];
    Random random = new Random();
    while ( true )
    {
        random.nextBytes( key );
        db.put( key, NULL );

        progress++;
        if ( ( progress % 100000 ) == 0 )
        {
            System.out.println( progress );
        }
    }
}

}

fails with this output:
100000
200000
300000
Exception in thread "main" org.iq80.leveldb.impl.DbImpl$BackgroundProcessingException: java.lang.IllegalArgumentException: key must be greater than last key
at org.iq80.leveldb.impl.DbImpl.checkBackgroundException(DbImpl.java:384)
at org.iq80.leveldb.impl.DbImpl.writeInternal(DbImpl.java:634)
at org.iq80.leveldb.impl.DbImpl.put(DbImpl.java:600)
at org.iq80.leveldb.impl.DbImpl.put(DbImpl.java:593)
at testleveldb.Test.main(Test.java:30)
Caused by: java.lang.IllegalArgumentException: key must be greater than last key
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:88)
at org.iq80.leveldb.table.BlockBuilder.add(BlockBuilder.java:109)
at org.iq80.leveldb.table.TableBuilder.add(TableBuilder.java:143)
at org.iq80.leveldb.impl.DbImpl.buildTable(DbImpl.java:928)
at org.iq80.leveldb.impl.DbImpl.writeLevel0Table(DbImpl.java:891)
at org.iq80.leveldb.impl.DbImpl.compactMemTableInternal(DbImpl.java:860)
at org.iq80.leveldb.impl.DbImpl.backgroundCompaction(DbImpl.java:425)
at org.iq80.leveldb.impl.DbImpl.backgroundCall(DbImpl.java:399)
at org.iq80.leveldb.impl.DbImpl.access$100(DbImpl.java:66)
at org.iq80.leveldb.impl.DbImpl$2.call(DbImpl.java:369)
at org.iq80.leveldb.impl.DbImpl$2.call(DbImpl.java:363)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)

Varying the writeBufferSize, the number of records where the error occurs changes.

I run this with leveldb 0.3 and 0.4-SNAPSHOT and the behavior is the same.

This was caused by the re-use of the byte array for the key. The jni+native implementation makes a copy of the key/value when it passes the values to the native code, so the same code works in that case.

I will close this issue as I don't expect leveldb to clone the key&value values. The only reason to make it work would be to match the behavior of the jni implementation. IMO, that behavior is just a side effect of the implementation, so I don't expect this issue to be fixed.