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.