@ -19,6 +19,7 @@ import java.math.BigDecimal;
import java.util.Arrays ;
import java.util.Arrays ;
import java.util.List ;
import java.util.List ;
import java.util.Map ;
import java.util.Map ;
import java.util.concurrent.TimeUnit ;
import org.redisson.api.RBitSetAsync ;
import org.redisson.api.RBitSetAsync ;
import org.redisson.api.RBloomFilter ;
import org.redisson.api.RBloomFilter ;
@ -54,15 +55,18 @@ public class RedissonBloomFilter<T> extends RedissonExpirable implements RBloomF
private volatile int hashIterations ;
private volatile int hashIterations ;
private final CommandExecutor commandExecutor ;
private final CommandExecutor commandExecutor ;
private final String configName ;
protected RedissonBloomFilter ( CommandExecutor commandExecutor , String name ) {
protected RedissonBloomFilter ( CommandExecutor commandExecutor , String name ) {
super ( commandExecutor , name ) ;
super ( commandExecutor , name ) ;
this . commandExecutor = commandExecutor ;
this . commandExecutor = commandExecutor ;
this . configName = suffixName ( getName ( ) , "config" ) ;
}
}
protected RedissonBloomFilter ( Codec codec , CommandExecutor commandExecutor , String name ) {
protected RedissonBloomFilter ( Codec codec , CommandExecutor commandExecutor , String name ) {
super ( codec , commandExecutor , name ) ;
super ( codec , commandExecutor , name ) ;
this . commandExecutor = commandExecutor ;
this . commandExecutor = commandExecutor ;
this . configName = suffixName ( getName ( ) , "config" ) ;
}
}
private int optimalNumOfHashFunctions ( long n , long m ) {
private int optimalNumOfHashFunctions ( long n , long m ) {
@ -179,18 +183,18 @@ public class RedissonBloomFilter<T> extends RedissonExpirable implements RBloomF
}
}
private void addConfigCheck ( int hashIterations , long size , CommandBatchService executorService ) {
private void addConfigCheck ( int hashIterations , long size , CommandBatchService executorService ) {
executorService . evalReadAsync ( getConfigName( ) , codec , RedisCommands . EVAL_VOID ,
executorService . evalReadAsync ( configName , codec , RedisCommands . EVAL_VOID ,
"local size = redis.call('hget', KEYS[1], 'size');" +
"local size = redis.call('hget', KEYS[1], 'size');" +
"local hashIterations = redis.call('hget', KEYS[1], 'hashIterations');" +
"local hashIterations = redis.call('hget', KEYS[1], 'hashIterations');" +
"assert(size == ARGV[1] and hashIterations == ARGV[2], 'Bloom filter config has been changed')" ,
"assert(size == ARGV[1] and hashIterations == ARGV[2], 'Bloom filter config has been changed')" ,
Arrays . < Object > asList ( getConfigName( ) ) , size , hashIterations ) ;
Arrays . < Object > asList ( configName ) , size , hashIterations ) ;
}
}
@Override
@Override
public long count ( ) {
public long count ( ) {
CommandBatchService executorService = new CommandBatchService ( commandExecutor . getConnectionManager ( ) ) ;
CommandBatchService executorService = new CommandBatchService ( commandExecutor . getConnectionManager ( ) ) ;
RFuture < Map < String , String > > configFuture = executorService . readAsync ( getConfigName( ) , StringCodec . INSTANCE ,
RFuture < Map < String , String > > configFuture = executorService . readAsync ( configName , StringCodec . INSTANCE ,
new RedisCommand < Map < Object , Object > > ( "HGETALL" , new ObjectMapReplayDecoder ( ) ) , getConfigName( ) ) ;
new RedisCommand < Map < Object , Object > > ( "HGETALL" , new ObjectMapReplayDecoder ( ) ) , configName ) ;
RBitSetAsync bs = createBitSet ( executorService ) ;
RBitSetAsync bs = createBitSet ( executorService ) ;
RFuture < Long > cardinalityFuture = bs . cardinalityAsync ( ) ;
RFuture < Long > cardinalityFuture = bs . cardinalityAsync ( ) ;
executorService . execute ( ) ;
executorService . execute ( ) ;
@ -202,12 +206,12 @@ public class RedissonBloomFilter<T> extends RedissonExpirable implements RBloomF
@Override
@Override
public RFuture < Boolean > deleteAsync ( ) {
public RFuture < Boolean > deleteAsync ( ) {
return commandExecutor . writeAsync ( getName ( ) , RedisCommands . DEL_OBJECTS , getName ( ) , getConfigName( ) ) ;
return commandExecutor . writeAsync ( getName ( ) , RedisCommands . DEL_OBJECTS , getName ( ) , configName ) ;
}
}
private void readConfig ( ) {
private void readConfig ( ) {
RFuture < Map < String , String > > future = commandExecutor . readAsync ( getConfigName( ) , StringCodec . INSTANCE ,
RFuture < Map < String , String > > future = commandExecutor . readAsync ( configName , StringCodec . INSTANCE ,
new RedisCommand < Map < Object , Object > > ( "HGETALL" , new ObjectMapReplayDecoder ( ) ) , getConfigName( ) ) ;
new RedisCommand < Map < Object , Object > > ( "HGETALL" , new ObjectMapReplayDecoder ( ) ) , configName ) ;
Map < String , String > config = commandExecutor . get ( future ) ;
Map < String , String > config = commandExecutor . get ( future ) ;
readConfig ( config ) ;
readConfig ( config ) ;
@ -245,13 +249,13 @@ public class RedissonBloomFilter<T> extends RedissonExpirable implements RBloomF
hashIterations = optimalNumOfHashFunctions ( expectedInsertions , size ) ;
hashIterations = optimalNumOfHashFunctions ( expectedInsertions , size ) ;
CommandBatchService executorService = new CommandBatchService ( commandExecutor . getConnectionManager ( ) ) ;
CommandBatchService executorService = new CommandBatchService ( commandExecutor . getConnectionManager ( ) ) ;
executorService . evalReadAsync ( getConfigName( ) , codec , RedisCommands . EVAL_VOID ,
executorService . evalReadAsync ( configName , codec , RedisCommands . EVAL_VOID ,
"local size = redis.call('hget', KEYS[1], 'size');" +
"local size = redis.call('hget', KEYS[1], 'size');" +
"local hashIterations = redis.call('hget', KEYS[1], 'hashIterations');" +
"local hashIterations = redis.call('hget', KEYS[1], 'hashIterations');" +
"assert(size == false and hashIterations == false, 'Bloom filter config has been changed')" ,
"assert(size == false and hashIterations == false, 'Bloom filter config has been changed')" ,
Arrays . < Object > asList ( getConfigName( ) ) , size , hashIterations ) ;
Arrays . < Object > asList ( configName ) , size , hashIterations ) ;
executorService . writeAsync ( getConfigName( ) , StringCodec . INSTANCE ,
executorService . writeAsync ( configName , StringCodec . INSTANCE ,
new RedisCommand < Void > ( "HMSET" , new VoidReplayConvertor ( ) ) , getConfigName( ) ,
new RedisCommand < Void > ( "HMSET" , new VoidReplayConvertor ( ) ) , configName ,
"size" , size , "hashIterations" , hashIterations ,
"size" , size , "hashIterations" , hashIterations ,
"expectedInsertions" , expectedInsertions , "falseProbability" , BigDecimal . valueOf ( falseProbability ) . toPlainString ( ) ) ;
"expectedInsertions" , expectedInsertions , "falseProbability" , BigDecimal . valueOf ( falseProbability ) . toPlainString ( ) ) ;
try {
try {
@ -267,31 +271,53 @@ public class RedissonBloomFilter<T> extends RedissonExpirable implements RBloomF
return true ;
return true ;
}
}
private String getConfigName ( ) {
@Override
return suffixName ( getName ( ) , "config" ) ;
public RFuture < Boolean > expireAsync ( long timeToLive , TimeUnit timeUnit ) {
return commandExecutor . evalWriteAsync ( getName ( ) , LongCodec . INSTANCE , RedisCommands . EVAL_BOOLEAN ,
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return redis.call('pexpire', KEYS[2], ARGV[1]); " ,
Arrays . < Object > asList ( getName ( ) , configName ) ,
timeUnit . toMillis ( timeToLive ) ) ;
}
@Override
public RFuture < Boolean > expireAtAsync ( long timestamp ) {
return commandExecutor . evalWriteAsync ( getName ( ) , LongCodec . INSTANCE , RedisCommands . EVAL_BOOLEAN ,
"redis.call('pexpireat', KEYS[1], ARGV[1]); " +
"return redis.call('pexpireat', KEYS[2], ARGV[1]); " ,
Arrays . < Object > asList ( getName ( ) , configName ) ,
timestamp ) ;
}
@Override
public RFuture < Boolean > clearExpireAsync ( ) {
return commandExecutor . evalWriteAsync ( getName ( ) , LongCodec . INSTANCE , RedisCommands . EVAL_BOOLEAN ,
"redis.call('persist', KEYS[1]); " +
"return redis.call('persist', KEYS[2]); " ,
Arrays . < Object > asList ( getName ( ) , configName ) ) ;
}
}
@Override
@Override
public long getExpectedInsertions ( ) {
public long getExpectedInsertions ( ) {
Long result = commandExecutor . read ( getConfigName ( ) , LongCodec . INSTANCE , RedisCommands . HGET , getConfigName ( ) , "expectedInsertions" ) ;
Long result = commandExecutor . read ( configName , LongCodec . INSTANCE , RedisCommands . HGET , configName , "expectedInsertions" ) ;
return check ( result ) ;
return check ( result ) ;
}
}
@Override
@Override
public double getFalseProbability ( ) {
public double getFalseProbability ( ) {
Double result = commandExecutor . read ( getConfigName ( ) , DoubleCodec . INSTANCE , RedisCommands . HGET , getConfigName ( ) , "falseProbability" ) ;
Double result = commandExecutor . read ( configName , DoubleCodec . INSTANCE , RedisCommands . HGET , configName , "falseProbability" ) ;
return check ( result ) ;
return check ( result ) ;
}
}
@Override
@Override
public long getSize ( ) {
public long getSize ( ) {
Long result = commandExecutor . read ( getConfigName ( ) , LongCodec . INSTANCE , RedisCommands . HGET , getConfigName ( ) , "size" ) ;
Long result = commandExecutor . read ( configName , LongCodec . INSTANCE , RedisCommands . HGET , configName , "size" ) ;
return check ( result ) ;
return check ( result ) ;
}
}
@Override
@Override
public int getHashIterations ( ) {
public int getHashIterations ( ) {
Integer result = commandExecutor . read ( getConfigName( ) , IntegerCodec . INSTANCE , RedisCommands . HGET , getConfigName( ) , "hashIterations" ) ;
Integer result = commandExecutor . read ( configName , IntegerCodec . INSTANCE , RedisCommands . HGET , configName , "hashIterations" ) ;
return check ( result ) ;
return check ( result ) ;
}
}