diff --git a/README.md b/README.md index 1864a4a..d98b224 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ echo "GET key1\r\n" | nc localhost 6379 Operation | Description ----------|------------ [APPEND](https://redis.io/commands/append) *key value* | If key already exists and is a string, this command appends the value at the end of the string. If key does not exist it is created and set as an empty string. +[AUTH](https://redis.io/commands/auth) *[username] password* | The AUTH command authenticates the current connection. [BGSAVE](https://redis.io/commands/bgsave) | Returns OK. [COMMAND](https://redis.io/commands/command) | Returns Array reply of details about all Redis commands. [COMMAND COUNT](https://redis.io/commands/command-count) | Returns Integer reply of number of total commands in this Redis server. @@ -50,7 +51,9 @@ Operation | Description [DECR](https://redis.io/commands/decr) *key* | Decrements the number stored at key by one. [DECRBY](https://redis.io/commands/decrby) *key decrement* | Decrements the number stored at key by decrement. [DEL](https://redis.io/commands/del) *key* | Removes the specified key. +[DISCARD](https://redis.io/commands/discard) | Flushes all previously queued commands in a [transaction](https://redis.io/topics/transactions) and restores the connection state to normal. [ECHO](https://redis.io/commands/echo) *message* | Returns message. +[EXEC](https://redis.io/commands/exec) | Executes all previously queued commands in a [transaction](https://redis.io/topics/transactions) and restores the connection state to normal. [EXISTS](https://redis.io/commands/exists) *key [key ...]* | Returns if key exists. [EXPIRE](https://redis.io/commands/expire) *key seconds* | Set a timeout on key. After the timeout has expired, the key will automatically be deleted. [EXPIREAT](https://redis.io/commands/expireat) *key timestamp* | EXPIREAT has the same effect and semantic as EXPIRE, but instead of specifying the number of seconds representing the TTL (time to live), it takes an absolute Unix timestamp (seconds since January 1, 1970). @@ -86,6 +89,7 @@ Operation | Description [MGET](https://redis.io/commands/mget) *key [key ...]* | Returns the values of all specified keys. [MSET](https://redis.io/commands/mset) *key value [key value ...]* | Sets the given keys to their respective values. [MSETNX](https://redis.io/commands/msetnx) *key value [key value ...]* | Sets the given keys to their respective values. MSETNX will not perform any operation at all even if just a single key already exists. +[MULTI](https://redis.io/commands/multi) | Marks the start of a [transaction](https://redis.io/topics/transactions) block. Subsequent commands will be queued for execution using EXEC. [PERSIST](https://redis.io/commands/persist) *key* | Remove the existing timeout on key, turning the key from volatile (a key with an expire set) to persistent. [PEXPIRE](https://redis.io/commands/pexpire) *key milliseconds* | This command works exactly like EXPIRE but the time to live of the key is specified in milliseconds instead of seconds. [PEXPIREAT](https://redis.io/commands/pexpireat) *key milliseconds-timestamp* | PEXPIREAT has the same effect and semantic as EXPIREAT, but the Unix time at which the key will expire is specified in milliseconds instead of seconds. @@ -146,8 +150,9 @@ Operation | Description ## Known Limitations * A partial but growing list of Redis commands. See [Redis Command Coverage](#redis-command-coverage). + * By default, an Aerospike namespace does not allow for TTLs. Read [more](https://discuss.aerospike.com/t/faq-what-are-expiration-eviction-and-stop-writes/2311) on how to set up expiration and eviction support. * Like Redis Cluster, Skyhook supports a single Redis 'database 0', which maps to a single namespace and set in the Aerospike Database. - * Will not try try to implement the cluster operations sub-commands of `CLUSTER`, `CLIENT`, `CONFIG`, `MEMORY`, `MONITOR`, `LATENCY`. + * Will not try to implement the cluster operations sub-commands of `CLUSTER`, `CLIENT`, `CONFIG`, `MEMORY`, `MONITOR`, `LATENCY`. * No support for Pub/Sub commands. * No support for Lua scripts. @@ -200,7 +205,9 @@ Now the server is listening to the `config.redisPort` (default: 6379) and is rea | hostList | The host list to seed the Aerospike cluster. | localhost:3000 | | namespase | The Aerospike namespace. | test | | set | The Aerospike set name. | redis | -| bin | The Aerospike bin name to set values. | b | +| clientPolicy | The Aerospike Java client [ClientPolicy](https://docs.aerospike.com/apidocs/java/com/aerospike/client/policy/ClientPolicy.html) configuration properties. | ClientPolicyConfig | +| bin | The Aerospike value bin name. | b | +| typeBin | The Aerospike value [type](https://redis.io/topics/data-types) bin name. | t | | redisPort | The server port to bind to. | 6379 | | workerThreads[1](#worker-threads) | The Netty worker group size. | number of available cores | | bossThreads | The Netty acceptor group size. | 2 | @@ -213,4 +220,3 @@ Licensed under an Apache 2.0 License. This is an active open source project. You can contribute to it by trying Skyhook, providing feedback, reporting bugs, and implementing more Redis commands. - diff --git a/build.gradle.kts b/build.gradle.kts index b909200..d17695e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "com.aerospike" -version = "0.7.0" +version = "0.8.0" repositories { mavenCentral() @@ -35,7 +35,7 @@ extra["logbackVersion"] = "1.2.3" extra["jacksonVersion"] = "2.12.2" dependencies { - implementation("com.aerospike:aerospike-client:5.0.5") + implementation("com.aerospike:aerospike-client:5.1.0") implementation("io.netty:netty-all:${project.extra["nettyVersion"]}") implementation("io.netty:netty-codec-redis:${project.extra["nettyVersion"]}") implementation("com.google.inject:guice:5.0.1") @@ -49,6 +49,7 @@ dependencies { implementation("com.fasterxml.jackson.core:jackson-databind:${project.extra["jacksonVersion"]}") implementation("com.fasterxml.jackson.module:jackson-module-kotlin:${project.extra["jacksonVersion"]}") implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${project.extra["jacksonVersion"]}") + implementation("com.google.guava:guava:30.1.1-jre") testImplementation(kotlin("test-junit5")) testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.0") diff --git a/src/main/kotlin/com/aerospike/skyhook/SkyhookModule.kt b/src/main/kotlin/com/aerospike/skyhook/SkyhookModule.kt index b897f16..cdb1158 100644 --- a/src/main/kotlin/com/aerospike/skyhook/SkyhookModule.kt +++ b/src/main/kotlin/com/aerospike/skyhook/SkyhookModule.kt @@ -1,12 +1,13 @@ package com.aerospike.skyhook -import com.aerospike.client.AerospikeClient -import com.aerospike.client.Host -import com.aerospike.client.IAerospikeClient +import com.aerospike.client.async.EventLoops +import com.aerospike.client.async.NettyEventLoops import com.aerospike.client.async.NioEventLoops import com.aerospike.client.policy.ClientPolicy import com.aerospike.skyhook.config.ServerConfiguration import com.aerospike.skyhook.util.SystemUtils +import com.aerospike.skyhook.util.client.AerospikeClientPool +import com.aerospike.skyhook.util.client.AerospikeClientPoolImpl import com.google.inject.AbstractModule import com.google.inject.Provides import com.google.inject.name.Names @@ -19,6 +20,8 @@ import io.netty.channel.nio.NioEventLoopGroup import io.netty.channel.socket.ServerSocketChannel import io.netty.channel.socket.nio.NioServerSocketChannel import mu.KotlinLogging +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors import javax.inject.Inject import javax.inject.Singleton @@ -31,6 +34,7 @@ class SkyhookModule( } override fun configure() { + bind(AerospikeClientPool::class.java).to(AerospikeClientPoolImpl::class.java) bindEventLoops() } @@ -97,15 +101,28 @@ class SkyhookModule( @Provides @Singleton @Inject - fun aerospikeClient(): IAerospikeClient { + fun clientPolicy(): ClientPolicy { val clientPolicy = ClientPolicy() - clientPolicy.eventLoops = NioEventLoops(config.workerThreads) + clientPolicy.eventLoops = getClientEventLoops() + config.clientPolicy.user?.let { clientPolicy.user = it } + config.clientPolicy.password?.let { clientPolicy.password = it } + config.clientPolicy.authMode?.let { clientPolicy.authMode = it } + config.clientPolicy.timeout?.let { clientPolicy.timeout = it } + return clientPolicy + } - return AerospikeClient( - clientPolicy, *Host.parseHosts( - config.hostList, - 3000 - ) - ) + private fun getClientEventLoops(): EventLoops { + return when (SystemUtils.os) { + SystemUtils.OS.LINUX -> NettyEventLoops(EpollEventLoopGroup(config.workerThreads)) + SystemUtils.OS.MAC -> NettyEventLoops(KQueueEventLoopGroup(config.workerThreads)) + else -> NioEventLoops(config.workerThreads) + } + } + + @Provides + @Singleton + @Inject + fun executorService(): ExecutorService { + return Executors.newFixedThreadPool(config.workerThreads) } } diff --git a/src/main/kotlin/com/aerospike/skyhook/command/RedisCommand.kt b/src/main/kotlin/com/aerospike/skyhook/command/RedisCommand.kt index c31217a..8759e83 100644 --- a/src/main/kotlin/com/aerospike/skyhook/command/RedisCommand.kt +++ b/src/main/kotlin/com/aerospike/skyhook/command/RedisCommand.kt @@ -1,135 +1,153 @@ package com.aerospike.skyhook.command -import com.aerospike.skyhook.util.RedisCommandsDetails.appendCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.bgsaveCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.commandCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.dbsizeCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.decrCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.decrbyCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.delCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.echoCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.existsCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.expireCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.expireatCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.flushallCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.flushdbCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.getCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.getsetCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hdelCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hexistsCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hgetCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hgetallCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hincrbyCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hincrbyfloatCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hkeysCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hlenCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hmgetCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hmsetCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hscanCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hsetCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hsetnxCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hstrlenCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.hvalsCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.incrCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.incrbyCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.incrbyfloatCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.lindexCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.llenCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.lolwutCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.lpopCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.lpushCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.lpushxCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.lrangeCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.mgetCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.msetCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.msetnxCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.persistCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.pexpireCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.pexpireatCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.pingCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.psetexCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.pttlCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.randomkeyCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.resetCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.rpopCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.rpushCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.rpushxCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.saddCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.saveCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.scanCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.scardCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.setCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.setexCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.setnxCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.sinterCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.sinterstoreCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.sismemberCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.smembersCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.sremCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.sscanCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.strlenCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.sunionCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.sunionstoreCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.timeCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.touchCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.ttlCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.typeCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.unlinkCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zaddCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zcardCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zcountCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zincrbyCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zlexcountCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zmscoreCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zpopmaxCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zpopminCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zrandmemberCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zrangeCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zrangebylexCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zrangebyscoreCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zrangestoreCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zrankCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zremCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zremrangebylexCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zremrangebyrankCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zremrangebyscoreCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zrevrangeCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zrevrangebylexCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zrevrangebyscoreCommand -import com.aerospike.skyhook.util.RedisCommandsDetails.zscanCommand +import com.aerospike.skyhook.command.CommandsDetails.appendCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.authCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.bgsaveCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.commandCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.dbsizeCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.decrCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.decrbyCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.delCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.discardCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.echoCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.execCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.existsCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.expireCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.expireatCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.flushallCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.flushdbCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.getCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.getsetCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hdelCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hexistsCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hgetCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hgetallCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hincrbyCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hincrbyfloatCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hkeysCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hlenCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hmgetCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hmsetCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hscanCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hsetCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hsetnxCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hstrlenCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.hvalsCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.incrCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.incrbyCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.incrbyfloatCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.lindexCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.llenCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.lolwutCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.lpopCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.lpushCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.lpushxCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.lrangeCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.mgetCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.msetCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.msetnxCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.multiCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.persistCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.pexpireCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.pexpireatCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.pingCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.psetexCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.pttlCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.randomkeyCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.resetCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.rpopCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.rpushCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.rpushxCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.saddCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.saveCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.scanCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.scardCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.setCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.setexCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.setnxCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.sinterCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.sinterstoreCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.sismemberCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.smembersCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.sremCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.sscanCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.strlenCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.sunionCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.sunionstoreCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.timeCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.touchCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.ttlCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.typeCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.unlinkCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zaddCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zcardCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zcountCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zincrbyCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zlexcountCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zmscoreCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zpopmaxCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zpopminCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zrandmemberCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zrangeCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zrangebylexCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zrangebyscoreCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zrangestoreCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zrankCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zremCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zremrangebylexCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zremrangebyrankCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zremrangebyscoreCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zrevrangeCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zrevrangebylexCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zrevrangebyscoreCommandDetails +import com.aerospike.skyhook.command.CommandsDetails.zscanCommandDetails +import com.aerospike.skyhook.handler.CommandHandler +import com.aerospike.skyhook.handler.aerospike.* +import com.aerospike.skyhook.handler.redis.* +import com.aerospike.skyhook.listener.key.* +import com.aerospike.skyhook.listener.list.* +import com.aerospike.skyhook.listener.map.* +import com.aerospike.skyhook.listener.scan.HscanCommandListener +import com.aerospike.skyhook.listener.scan.ScanCommandListener +import com.aerospike.skyhook.listener.scan.SscanCommandListener +import com.aerospike.skyhook.listener.scan.ZscanCommandListener import io.netty.channel.ChannelHandlerContext import io.netty.handler.codec.redis.ArrayHeaderRedisMessage import mu.KotlinLogging import java.util.* +import kotlin.reflect.KFunction1 -enum class RedisCommand(private val details: RedisCommandDetails?) { - GET(getCommand), - MGET(mgetCommand), - GETSET(getsetCommand), - SET(setCommand), - SETEX(setexCommand), - PSETEX(psetexCommand), - SETNX(setnxCommand), - MSET(msetCommand), - MSETNX(msetnxCommand), - EXISTS(existsCommand), - EXPIRE(expireCommand), - PEXPIRE(pexpireCommand), - EXPIREAT(expireatCommand), - PEXPIREAT(pexpireatCommand), - PERSIST(persistCommand), - APPEND(appendCommand), - INCR(incrCommand), - INCRBY(incrbyCommand), - INCRBYFLOAT(incrbyfloatCommand), - DECR(decrCommand), - DECRBY(decrbyCommand), - STRLEN(strlenCommand), - TTL(ttlCommand), - PTTL(pttlCommand), - DEL(delCommand), - UNLINK(unlinkCommand), - RANDOMKEY(randomkeyCommand), +enum class RedisCommand( + val details: RedisCommandDetails?, + val newHandler: KFunction1 +) { + GET(getCommandDetails, ::GetCommandListener), + MGET(mgetCommandDetails, ::MgetCommandListener), + GETSET(getsetCommandDetails, ::GetsetCommandListener), + SET(setCommandDetails, ::SetCommandListener), + SETEX(setexCommandDetails, ::SetCommandListener), + PSETEX(psetexCommandDetails, ::SetCommandListener), + SETNX(setnxCommandDetails, ::SetCommandListener), + MSET(msetCommandDetails, ::MsetCommandListener), + MSETNX(msetnxCommandDetails, ::MsetCommandListener), + EXISTS(existsCommandDetails, ::ExistsCommandListener), + EXPIRE(expireCommandDetails, ::ExpireCommandListener), + PEXPIRE(pexpireCommandDetails, ::ExpireCommandListener), + EXPIREAT(expireatCommandDetails, ::ExpireCommandListener), + PEXPIREAT(pexpireatCommandDetails, ::ExpireCommandListener), + PERSIST(persistCommandDetails, ::ExpireCommandListener), + APPEND(appendCommandDetails, ::AppendCommandListener), + INCR(incrCommandDetails, ::IncrCommandListener), + INCRBY(incrbyCommandDetails, ::IncrCommandListener), + INCRBYFLOAT(incrbyfloatCommandDetails, ::IncrCommandListener), + DECR(decrCommandDetails, ::DecrCommandListener), + DECRBY(decrbyCommandDetails, ::DecrCommandListener), + STRLEN(strlenCommandDetails, ::StrlenCommandListener), + TTL(ttlCommandDetails, ::TtlCommandListener), + PTTL(pttlCommandDetails, ::TtlCommandListener), + DEL(delCommandDetails, ::DelCommandListener), + UNLINK(unlinkCommandDetails, ::DelCommandListener), + RANDOMKEY(randomkeyCommandDetails, ::RandomkeyCommandListener), /** * The Redis TOUCH command changes the last_access_time of the key (used for LRU eviction). @@ -137,85 +155,88 @@ enum class RedisCommand(private val details: RedisCommandDetails?) { * The actual implementation returns the number of records that were 'touched' using * [com.aerospike.client.AerospikeClient.exists]. */ - TOUCH(touchCommand), - TYPE(typeCommand), - - LPUSH(lpushCommand), - LPUSHX(lpushxCommand), - RPUSH(rpushCommand), - RPUSHX(rpushxCommand), - LINDEX(lindexCommand), - LLEN(llenCommand), - LPOP(lpopCommand), - RPOP(rpopCommand), - LRANGE(lrangeCommand), - - HSET(hsetCommand), - HSETNX(hsetnxCommand), - HMSET(hmsetCommand), - SADD(saddCommand), - HEXISTS(hexistsCommand), - SISMEMBER(sismemberCommand), - HGET(hgetCommand), - HMGET(hmgetCommand), - HGETALL(hgetallCommand), - HVALS(hvalsCommand), - HKEYS(hkeysCommand), - ZMSCORE(zmscoreCommand), - ZRANK(zrankCommand), - SMEMBERS(smembersCommand), - HINCRBY(hincrbyCommand), - HINCRBYFLOAT(hincrbyfloatCommand), - ZINCRBY(zincrbyCommand), - HSTRLEN(hstrlenCommand), - HLEN(hlenCommand), - SCARD(scardCommand), - ZCARD(zcardCommand), - HDEL(hdelCommand), - SREM(sremCommand), - ZREM(zremCommand), - SUNION(sunionCommand), - SINTER(sinterCommand), - SUNIONSTORE(sunionstoreCommand), - SINTERSTORE(sinterstoreCommand), - ZADD(zaddCommand), - ZPOPMAX(zpopmaxCommand), - ZPOPMIN(zpopminCommand), - ZRANDMEMBER(zrandmemberCommand), - ZCOUNT(zcountCommand), - ZLEXCOUNT(zlexcountCommand), - ZREMRANGEBYSCORE(zremrangebyscoreCommand), - ZREMRANGEBYRANK(zremrangebyrankCommand), - ZREMRANGEBYLEX(zremrangebylexCommand), - ZRANGE(zrangeCommand), - ZRANGESTORE(zrangestoreCommand), - ZREVRANGE(zrevrangeCommand), - ZRANGEBYSCORE(zrangebyscoreCommand), - ZREVRANGEBYSCORE(zrevrangebyscoreCommand), - ZRANGEBYLEX(zrangebylexCommand), - ZREVRANGEBYLEX(zrevrangebylexCommand), - - SCAN(scanCommand), - HSCAN(hscanCommand), - SSCAN(sscanCommand), - ZSCAN(zscanCommand), - - FLUSHDB(flushdbCommand), - FLUSHALL(flushallCommand), - DBSIZE(dbsizeCommand), - - PING(pingCommand), - ECHO(echoCommand), - LOLWUT(lolwutCommand), - TIME(timeCommand), - QUIT(null), - RESET(resetCommand), - SAVE(saveCommand), - BGSAVE(bgsaveCommand), - - COMMAND(commandCommand), - - UNKNOWN(null); + TOUCH(touchCommandDetails, ::ExistsCommandListener), + TYPE(typeCommandDetails, ::TypeCommandListener), + + LPUSH(lpushCommandDetails, ::ListPushCommandListener), + LPUSHX(lpushxCommandDetails, ::ListPushCommandListener), + RPUSH(rpushCommandDetails, ::ListPushCommandListener), + RPUSHX(rpushxCommandDetails, ::ListPushCommandListener), + LINDEX(lindexCommandDetails, ::LindexCommandListener), + LLEN(llenCommandDetails, ::LlenCommandListener), + LPOP(lpopCommandDetails, ::ListPopCommandListener), + RPOP(rpopCommandDetails, ::ListPopCommandListener), + LRANGE(lrangeCommandDetails, ::LrangeCommandListener), + + HSET(hsetCommandDetails, ::HsetCommandListener), + HSETNX(hsetnxCommandDetails, ::HsetnxCommandListener), + HMSET(hmsetCommandDetails, ::HmsetCommandListener), + SADD(saddCommandDetails, ::SaddCommandListener), + HEXISTS(hexistsCommandDetails, ::HexistsCommandListener), + SISMEMBER(sismemberCommandDetails, ::HexistsCommandListener), + HGET(hgetCommandDetails, ::MapGetCommandListener), + HMGET(hmgetCommandDetails, ::MapGetCommandListener), + HGETALL(hgetallCommandDetails, ::MapGetCommandListener), + HVALS(hvalsCommandDetails, ::MapGetCommandListener), + HKEYS(hkeysCommandDetails, ::MapGetCommandListener), + ZMSCORE(zmscoreCommandDetails, ::MapGetCommandListener), + ZRANK(zrankCommandDetails, ::MapGetCommandListener), + SMEMBERS(smembersCommandDetails, ::MapGetCommandListener), + HINCRBY(hincrbyCommandDetails, ::HincrbyCommandListener), + HINCRBYFLOAT(hincrbyfloatCommandDetails, ::HincrbyCommandListener), + ZINCRBY(zincrbyCommandDetails, ::HincrbyCommandListener), + HSTRLEN(hstrlenCommandDetails, ::HstrlenCommandListener), + HLEN(hlenCommandDetails, ::MapSizeCommandListener), + SCARD(scardCommandDetails, ::MapSizeCommandListener), + ZCARD(zcardCommandDetails, ::MapSizeCommandListener), + HDEL(hdelCommandDetails, ::MapDelCommandListener), + SREM(sremCommandDetails, ::MapDelCommandListener), + ZREM(zremCommandDetails, ::MapDelCommandListener), + SUNION(sunionCommandDetails, ::SunionCommandListener), + SINTER(sinterCommandDetails, ::SinterCommandListener), + SUNIONSTORE(sunionstoreCommandDetails, ::SunionstoreCommandListener), + SINTERSTORE(sinterstoreCommandDetails, ::SinterstoreCommandListener), + ZADD(zaddCommandDetails, ::ZaddCommandListener), + ZPOPMAX(zpopmaxCommandDetails, ::ZpopmaxCommandListener), + ZPOPMIN(zpopminCommandDetails, ::ZpopminCommandListener), + ZRANDMEMBER(zrandmemberCommandDetails, ::ZrandmemberCommandListener), + ZCOUNT(zcountCommandDetails, ::ZcountCommandListener), + ZLEXCOUNT(zlexcountCommandDetails, ::ZlexcountCommandListener), + ZREMRANGEBYSCORE(zremrangebyscoreCommandDetails, ::ZremrangebyscoreCommandListener), + ZREMRANGEBYRANK(zremrangebyrankCommandDetails, ::ZremrangebyrankCommandListener), + ZREMRANGEBYLEX(zremrangebylexCommandDetails, ::ZremrangebylexCommandListener), + ZRANGE(zrangeCommandDetails, ::ZrangeCommandListener), + ZRANGESTORE(zrangestoreCommandDetails, ::ZrangestoreCommandListener), + ZREVRANGE(zrevrangeCommandDetails, ::ZrevrangeCommandListener), + ZRANGEBYSCORE(zrangebyscoreCommandDetails, ::ZrangebyscoreCommandListener), + ZREVRANGEBYSCORE(zrevrangebyscoreCommandDetails, ::ZrevrangebyscoreCommandListener), + ZRANGEBYLEX(zrangebylexCommandDetails, ::ZrangebylexCommandListener), + ZREVRANGEBYLEX(zrevrangebylexCommandDetails, ::ZrevrangebylexCommandListener), + + SCAN(scanCommandDetails, ::ScanCommandListener), + HSCAN(hscanCommandDetails, ::HscanCommandListener), + SSCAN(sscanCommandDetails, ::SscanCommandListener), + ZSCAN(zscanCommandDetails, ::ZscanCommandListener), + + FLUSHDB(flushdbCommandDetails, ::FlushCommandHandler), + FLUSHALL(flushallCommandDetails, ::FlushCommandHandler), + DBSIZE(dbsizeCommandDetails, ::DbsizeCommandHandler), + + PING(pingCommandDetails, ::PingCommandHandler), + ECHO(echoCommandDetails, ::EchoCommandHandler), + LOLWUT(lolwutCommandDetails, ::LolwutCommandHandler), + TIME(timeCommandDetails, ::TimeCommandHandler), + QUIT(null, ::MockCommandHandler), + RESET(resetCommandDetails, ::MockCommandHandler), + SAVE(saveCommandDetails, ::MockCommandHandler), + BGSAVE(bgsaveCommandDetails, ::MockCommandHandler), + AUTH(authCommandDetails, ::AuthCommandHandler), + + MULTI(multiCommandDetails, ::MultiCommandHandler), + DISCARD(discardCommandDetails, ::DiscardCommandHandler), + EXEC(execCommandDetails, ::ExecCommandHandler), + + COMMAND(commandCommandDetails, ::CommandCommandHandler); companion object { private val log = KotlinLogging.logger {} @@ -223,9 +244,10 @@ enum class RedisCommand(private val details: RedisCommandDetails?) { fun getValue(stringValue: String): RedisCommand { return try { valueOf(stringValue.toUpperCase(Locale.ENGLISH)) - } catch (ignore: IllegalArgumentException) { - log.warn { "$stringValue unsupported command" } - UNKNOWN + } catch (e: IllegalArgumentException) { + val msg = "ERR $stringValue unsupported command" + log.warn { msg } + throw UnsupportedOperationException(msg) } } @@ -247,3 +269,148 @@ enum class RedisCommand(private val details: RedisCommandDetails?) { } } } + +object CommandsDetails { + + val getCommandDetails = RedisCommandDetails("get", 2, arrayListOf("readonly", "fast"), 1, 1, 1) + val mgetCommandDetails = RedisCommandDetails("mget", -2, arrayListOf("readonly", "fast"), 1, -1, 1) + val getsetCommandDetails = RedisCommandDetails("getset", 3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val setCommandDetails = RedisCommandDetails("set", -3, arrayListOf("write", "denyoom"), 1, 1, 1) + val setexCommandDetails = RedisCommandDetails("setex", 4, arrayListOf("write", "denyoom"), 1, 1, 1) + val psetexCommandDetails = RedisCommandDetails("psetex", 4, arrayListOf("write", "denyoom"), 1, 1, 1) + val setnxCommandDetails = RedisCommandDetails("setnx", 3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val msetCommandDetails = RedisCommandDetails("mset", -3, arrayListOf("write", "denyoom"), 1, -1, 2) + val msetnxCommandDetails = RedisCommandDetails("msetnx", -3, arrayListOf("write", "denyoom"), 1, -1, 2) + val existsCommandDetails = RedisCommandDetails("exists", -2, arrayListOf("readonly", "fast"), 1, -1, 1) + val expireCommandDetails = RedisCommandDetails("expire", 3, arrayListOf("write", "fast"), 1, 1, 1) + val pexpireCommandDetails = RedisCommandDetails("pexpire", 3, arrayListOf("write", "fast"), 1, 1, 1) + val expireatCommandDetails = RedisCommandDetails("expireat", 3, arrayListOf("write", "fast"), 1, 1, 1) + val pexpireatCommandDetails = RedisCommandDetails("pexpireat", 3, arrayListOf("write", "fast"), 1, 1, 1) + val persistCommandDetails = RedisCommandDetails("persist", 2, arrayListOf("write", "fast"), 1, 1, 1) + val appendCommandDetails = RedisCommandDetails("append", 3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val incrCommandDetails = RedisCommandDetails("incr", 2, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val incrbyCommandDetails = RedisCommandDetails("incrby", 3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val incrbyfloatCommandDetails = + RedisCommandDetails("incrbyfloat", 3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val decrCommandDetails = RedisCommandDetails("decr", 2, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val decrbyCommandDetails = RedisCommandDetails("decrby", 3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val strlenCommandDetails = RedisCommandDetails("strlen", 2, arrayListOf("readonly", "fast"), 1, 1, 1) + val ttlCommandDetails = RedisCommandDetails("ttl", 2, arrayListOf("readonly", "random", "fast"), 1, 1, 1) + val pttlCommandDetails = RedisCommandDetails("pttl", 2, arrayListOf("readonly", "random", "fast"), 1, 1, 1) + val delCommandDetails = RedisCommandDetails("del", -2, arrayListOf("write"), 1, -1, 1) + val unlinkCommandDetails = RedisCommandDetails("unlink", -2, arrayListOf("write", "fast"), 1, -1, 1) + val randomkeyCommandDetails = RedisCommandDetails("randomkey", 1, arrayListOf("readonly", "random"), 0, 0, 0) + val touchCommandDetails = RedisCommandDetails("touch", -2, arrayListOf("readonly", "fast"), 1, -1, 1) + val typeCommandDetails = RedisCommandDetails("type", 2, arrayListOf("readonly", "fast"), 1, 1, 1) + + val lpushCommandDetails = RedisCommandDetails("lpush", -3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val lpushxCommandDetails = RedisCommandDetails("lpushx", -3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val rpushCommandDetails = RedisCommandDetails("rpush", -3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val rpushxCommandDetails = RedisCommandDetails("rpushx", -3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val lindexCommandDetails = RedisCommandDetails("lindex", 3, arrayListOf("readonly"), 1, 1, 1) + val llenCommandDetails = RedisCommandDetails("llen", 2, arrayListOf("readonly", "fast"), 1, 1, 1) + val lpopCommandDetails = RedisCommandDetails("lpop", -2, arrayListOf("write", "fast"), 1, 1, 1) + val rpopCommandDetails = RedisCommandDetails("rpop", -2, arrayListOf("write", "fast"), 1, 1, 1) + val lrangeCommandDetails = RedisCommandDetails("lrange", 4, arrayListOf("readonly"), 1, 1, 1) + + val hsetCommandDetails = RedisCommandDetails("hset", -4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val hsetnxCommandDetails = RedisCommandDetails("hsetnx", 4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val hmsetCommandDetails = RedisCommandDetails("hmset", -4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val saddCommandDetails = RedisCommandDetails("sadd", -3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val hexistsCommandDetails = RedisCommandDetails("hexists", 3, arrayListOf("readonly", "fast"), 1, 1, 1) + val sismemberCommandDetails = RedisCommandDetails("sismember", 3, arrayListOf("readonly", "fast"), 1, 1, 1) + val hgetCommandDetails = RedisCommandDetails("hget", 3, arrayListOf("readonly", "fast"), 1, 1, 1) + val hmgetCommandDetails = RedisCommandDetails("hmget", -3, arrayListOf("readonly", "fast"), 1, 1, 1) + val hgetallCommandDetails = RedisCommandDetails("hgetall", 2, arrayListOf("readonly", "random"), 1, 1, 1) + val hvalsCommandDetails = RedisCommandDetails("hvals", 2, arrayListOf("readonly", "sort_for_script"), 1, 1, 1) + val hkeysCommandDetails = RedisCommandDetails("hkeys", 2, arrayListOf("readonly", "sort_for_script"), 1, 1, 1) + val smembersCommandDetails = RedisCommandDetails("smembers", 2, arrayListOf("readonly", "sort_for_script"), 1, 1, 1) + val hincrbyCommandDetails = RedisCommandDetails("hincrby", 4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val hincrbyfloatCommandDetails = + RedisCommandDetails("hincrbyfloat", 4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val hstrlenCommandDetails = RedisCommandDetails("hstrlen", 3, arrayListOf("readonly", "fast"), 1, 1, 1) + val hlenCommandDetails = RedisCommandDetails("hlen", 2, arrayListOf("readonly", "fast"), 1, 1, 1) + val scardCommandDetails = RedisCommandDetails("scard", 2, arrayListOf("readonly", "fast"), 1, 1, 1) + val zcardCommandDetails = RedisCommandDetails("zcard", 2, arrayListOf("readonly", "fast"), 1, 1, 1) + val hdelCommandDetails = RedisCommandDetails("hdel", -3, arrayListOf("write", "fast"), 1, 1, 1) + val sremCommandDetails = RedisCommandDetails("srem", -3, arrayListOf("write", "fast"), 1, 1, 1) + val zremCommandDetails = RedisCommandDetails("zrem", -3, arrayListOf("write", "fast"), 1, 1, 1) + val sunionCommandDetails = RedisCommandDetails("sunion", -2, arrayListOf("readonly", "sort_for_script"), 1, -1, 1) + val sinterCommandDetails = RedisCommandDetails("sinter", -2, arrayListOf("readonly", "sort_for_script"), 1, -1, 1) + val sunionstoreCommandDetails = RedisCommandDetails("sunionstore", -3, arrayListOf("write", "denyoom"), 1, -1, 1) + val sinterstoreCommandDetails = RedisCommandDetails("sinterstore", -3, arrayListOf("write", "denyoom"), 1, -1, 1) + val zmscoreCommandDetails = RedisCommandDetails("zmscore", -3, arrayListOf("readonly", "fast"), 1, 1, 1) + val zrankCommandDetails = RedisCommandDetails("zrank", 3, arrayListOf("readonly", "fast"), 1, 1, 1) + val zincrbyCommandDetails = RedisCommandDetails("zincrby", 4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val zaddCommandDetails = RedisCommandDetails("zadd", -4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) + val zpopmaxCommandDetails = RedisCommandDetails("zpopmax", -2, arrayListOf("write", "fast"), 1, 1, 1) + val zpopminCommandDetails = RedisCommandDetails("zpopmin", -2, arrayListOf("write", "fast"), 1, 1, 1) + val zrandmemberCommandDetails = RedisCommandDetails("zrandmember", -2, arrayListOf("readonly", "random"), 1, 1, 1) + val zcountCommandDetails = RedisCommandDetails("zcount", 4, arrayListOf("readonly", "fast"), 1, 1, 1) + val zlexcountCommandDetails = RedisCommandDetails("zlexcount", 4, arrayListOf("readonly", "fast"), 1, 1, 1) + val zremrangebyscoreCommandDetails = RedisCommandDetails("zremrangebyscore", 4, arrayListOf("write"), 1, 1, 1) + val zremrangebyrankCommandDetails = RedisCommandDetails("zremrangebyrank", 4, arrayListOf("write"), 1, 1, 1) + val zremrangebylexCommandDetails = RedisCommandDetails("zremrangebylex", 4, arrayListOf("write"), 1, 1, 1) + val zrangeCommandDetails = RedisCommandDetails("zrange", -4, arrayListOf("readonly"), 1, 1, 1) + val zrangestoreCommandDetails = RedisCommandDetails("zrangestore", -5, arrayListOf("write", "denyoom"), 1, 2, 1) + val zrevrangeCommandDetails = RedisCommandDetails("zrevrange", -4, arrayListOf("readonly"), 1, 1, 1) + val zrangebyscoreCommandDetails = RedisCommandDetails("zrangebyscore", -4, arrayListOf("readonly"), 1, 1, 1) + val zrevrangebyscoreCommandDetails = RedisCommandDetails("zrevrangebyscore", -4, arrayListOf("readonly"), 1, 1, 1) + val zrangebylexCommandDetails = RedisCommandDetails("zrangebylex", -4, arrayListOf("readonly"), 1, 1, 1) + val zrevrangebylexCommandDetails = RedisCommandDetails("zrevrangebylex", -4, arrayListOf("readonly"), 1, 1, 1) + + val scanCommandDetails = RedisCommandDetails("scan", -2, arrayListOf("readonly", "random"), 0, 0, 0) + val hscanCommandDetails = RedisCommandDetails("hscan", -3, arrayListOf("readonly", "random"), 1, 1, 1) + val sscanCommandDetails = RedisCommandDetails("sscan", -3, arrayListOf("readonly", "random"), 1, 1, 1) + val zscanCommandDetails = RedisCommandDetails("zscan", -3, arrayListOf("readonly", "random"), 1, 1, 1) + + val flushdbCommandDetails = RedisCommandDetails("flushdb", -1, arrayListOf("write"), 0, 0, 0) + val flushallCommandDetails = RedisCommandDetails("flushall", -1, arrayListOf("write"), 0, 0, 0) + val dbsizeCommandDetails = RedisCommandDetails("dbsize", 1, arrayListOf("readonly", "fast"), 0, 0, 0) + + val pingCommandDetails = RedisCommandDetails("ping", -1, arrayListOf("stale", "fast"), 0, 0, 0) + val echoCommandDetails = RedisCommandDetails("echo", 2, arrayListOf("fast"), 0, 0, 0) + val lolwutCommandDetails = RedisCommandDetails("lolwut", -1, arrayListOf("readonly", "fast"), 0, 0, 0) + val timeCommandDetails = RedisCommandDetails("time", 1, arrayListOf("random", "loading", "stale", "fast"), 0, 0, 0) + val resetCommandDetails = + RedisCommandDetails("reset", 1, arrayListOf("noscript", "loading", "stale", "fast"), 0, 0, 0) + val saveCommandDetails = RedisCommandDetails("save", 1, arrayListOf("admin", "noscript"), 0, 0, 0) + val bgsaveCommandDetails = RedisCommandDetails("bgsave", -1, arrayListOf("admin", "noscript"), 0, 0, 0) + val commandCommandDetails = RedisCommandDetails("command", -1, arrayListOf("random", "loading", "stale"), 0, 0, 0) + + val authCommandDetails = RedisCommandDetails( + "auth", + -2, + arrayListOf("noscript", "loading", "stale", "skip_monitor", "skip_slowlog", "fast", "no_auth"), + 0, + 0, + 0 + ) + + val multiCommandDetails = RedisCommandDetails( + "multi", + 1, + arrayListOf("noscript", "loading", "stale", "fast"), + 0, + 0, + 0 + ) + + val discardCommandDetails = RedisCommandDetails( + "discard", + 1, + arrayListOf("noscript", "loading", "stale", "fast"), + 0, + 0, + 0 + ) + + val execCommandDetails = RedisCommandDetails( + "exec", + 1, + arrayListOf("noscript", "loading", "stale", "skip_monitor", "skip_slowlog"), + 0, + 0, + 0 + ) +} diff --git a/src/main/kotlin/com/aerospike/skyhook/command/RedisCommandDetails.kt b/src/main/kotlin/com/aerospike/skyhook/command/RedisCommandDetails.kt index 7499d4f..153cc5e 100644 --- a/src/main/kotlin/com/aerospike/skyhook/command/RedisCommandDetails.kt +++ b/src/main/kotlin/com/aerospike/skyhook/command/RedisCommandDetails.kt @@ -10,15 +10,16 @@ data class RedisCommandDetails( val firstKeyPosition: Int, val lastKeyPosition: Int, val stepCount: Int -) : NettyResponseWriter() { +) { fun write(ctx: ChannelHandlerContext) { - writeArrayHeader(ctx, 6) - writeSimpleString(ctx, commandName) - writeLong(ctx, commandArity) - writeObjectListStr(ctx, commandFlags) - writeLong(ctx, firstKeyPosition) - writeLong(ctx, lastKeyPosition) - writeLong(ctx, stepCount) + val nrw = NettyResponseWriter(ctx) + nrw.writeArrayHeader(6) + nrw.writeSimpleString(commandName) + nrw.writeLong(commandArity) + nrw.writeObjectListStr(commandFlags) + nrw.writeLong(firstKeyPosition) + nrw.writeLong(lastKeyPosition) + nrw.writeLong(stepCount) } } diff --git a/src/main/kotlin/com/aerospike/skyhook/command/RequestCommand.kt b/src/main/kotlin/com/aerospike/skyhook/command/RequestCommand.kt index d2d3687..2399175 100644 --- a/src/main/kotlin/com/aerospike/skyhook/command/RequestCommand.kt +++ b/src/main/kotlin/com/aerospike/skyhook/command/RequestCommand.kt @@ -12,7 +12,12 @@ data class RequestCommand( Value.get(String(args[1])) } - val command: RedisCommand by lazy { + val command: RedisCommand = RedisCommand.getValue(String(args[0])) + + val transactional: Boolean by lazy { + command == RedisCommand.MULTI || + command == RedisCommand.EXEC || + command == RedisCommand.DISCARD } } diff --git a/src/main/kotlin/com/aerospike/skyhook/config/AerospikeContext.kt b/src/main/kotlin/com/aerospike/skyhook/config/AerospikeContext.kt index 1fa0d7b..1176121 100644 --- a/src/main/kotlin/com/aerospike/skyhook/config/AerospikeContext.kt +++ b/src/main/kotlin/com/aerospike/skyhook/config/AerospikeContext.kt @@ -1,14 +1,7 @@ package com.aerospike.skyhook.config -import com.aerospike.client.IAerospikeClient - data class AerospikeContext( - /** - * The Aerospike client instance. - */ - val client: IAerospikeClient, - /** * The Aerospike namespace to map to. */ diff --git a/src/main/kotlin/com/aerospike/skyhook/config/ClientPolicyConfig.kt b/src/main/kotlin/com/aerospike/skyhook/config/ClientPolicyConfig.kt new file mode 100644 index 0000000..0f29c22 --- /dev/null +++ b/src/main/kotlin/com/aerospike/skyhook/config/ClientPolicyConfig.kt @@ -0,0 +1,30 @@ +package com.aerospike.skyhook.config + +import com.aerospike.client.policy.AuthMode + +data class ClientPolicyConfig( + + /** + * User authentication to cluster. + * Leave null for clusters running without restricted access. + */ + val user: String? = null, + + /** + * Password authentication to cluster. + * The password will be stored by the client and sent to server in hashed format. + * Leave null for clusters running without restricted access. + */ + val password: String? = null, + + /** + * Authentication mode used when user/password is defined. + */ + val authMode: AuthMode? = null, + + /** + * Initial host connection timeout in milliseconds. + * The timeout when opening a connection to the server host for the first time. + */ + val timeout: Int? = null +) diff --git a/src/main/kotlin/com/aerospike/skyhook/config/ServerConfiguration.kt b/src/main/kotlin/com/aerospike/skyhook/config/ServerConfiguration.kt index 36a1767..abb30c3 100644 --- a/src/main/kotlin/com/aerospike/skyhook/config/ServerConfiguration.kt +++ b/src/main/kotlin/com/aerospike/skyhook/config/ServerConfiguration.kt @@ -17,6 +17,11 @@ data class ServerConfiguration( */ val set: String? = "redis", + /** + * Aerospike Client Policy configuration properties. + */ + val clientPolicy: ClientPolicyConfig = ClientPolicyConfig(), + /** * The Aerospike bin name to set values. */ diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/AerospikeChannelHandler.kt b/src/main/kotlin/com/aerospike/skyhook/handler/AerospikeChannelHandler.kt index 1eccb80..66b6645 100644 --- a/src/main/kotlin/com/aerospike/skyhook/handler/AerospikeChannelHandler.kt +++ b/src/main/kotlin/com/aerospike/skyhook/handler/AerospikeChannelHandler.kt @@ -1,6 +1,8 @@ package com.aerospike.skyhook.handler +import com.aerospike.client.AerospikeException import com.aerospike.skyhook.command.RequestCommand +import com.aerospike.skyhook.pipeline.AerospikeChannelInitializer.Companion.transactionAttrKey import io.netty.channel.ChannelHandler import io.netty.channel.ChannelHandlerContext import io.netty.channel.ChannelInboundHandlerAdapter @@ -10,14 +12,11 @@ import io.netty.util.CharsetUtil import io.netty.util.ReferenceCountUtil import mu.KotlinLogging import java.util.* -import javax.inject.Inject import javax.inject.Singleton @Singleton @ChannelHandler.Sharable -class AerospikeChannelHandler @Inject constructor( - private val nettyAerospikeHandler: NettyAerospikeHandler -) : ChannelInboundHandlerAdapter() { +class AerospikeChannelHandler() : ChannelInboundHandlerAdapter() { companion object { private val log = KotlinLogging.logger(this::class.java.name) @@ -38,7 +37,7 @@ class AerospikeChannelHandler @Inject constructor( } val cmd = RequestCommand(arguments) - nettyAerospikeHandler.handleCommand(cmd, ctx) + handleCommand(cmd, ctx) } is AbstractStringRedisMessage -> { @@ -46,16 +45,44 @@ class AerospikeChannelHandler @Inject constructor( (msg.content().split(" ") .map { it.encodeToByteArray() }).toList() ) - nettyAerospikeHandler.handleCommand(cmd, ctx) + handleCommand(cmd, ctx) } else -> log.warn { "Unsupported message type ${msg?.javaClass?.simpleName}" } } + } catch (e: UnsupportedOperationException) { + ctx.write(ErrorRedisMessage(e.message)) + ctx.flush() } finally { ReferenceCountUtil.release(msg) } } + /** + * Handle the input command. Listeners are responsible to send the response + * to the client. + */ + private fun handleCommand(cmd: RequestCommand, ctx: ChannelHandlerContext) { + try { + val state = ctx.channel().attr(transactionAttrKey).get() + if (state.inTransaction && !cmd.transactional) { + state.commands.addLast(cmd) + ctx.write(SimpleStringRedisMessage("QUEUED")) + ctx.flush() + } else { + cmd.command.newHandler(ctx).handle(cmd) + } + } catch (e: Exception) { + val msg = when (e) { + is AerospikeException -> "Internal error" + else -> e.message + } + log.warn(e) {} + ctx.write(ErrorRedisMessage(msg)) + ctx.flush() + } + } + private fun printAggregatedRedisResponse(msg: RedisMessage) { when (msg) { is SimpleStringRedisMessage -> { diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/CommandHandler.kt b/src/main/kotlin/com/aerospike/skyhook/handler/CommandHandler.kt index e2d4f9f..c5708fa 100644 --- a/src/main/kotlin/com/aerospike/skyhook/handler/CommandHandler.kt +++ b/src/main/kotlin/com/aerospike/skyhook/handler/CommandHandler.kt @@ -3,5 +3,9 @@ package com.aerospike.skyhook.handler import com.aerospike.skyhook.command.RequestCommand interface CommandHandler { + + /** + * Handles the incoming request command. + */ fun handle(cmd: RequestCommand) } diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/NettyAerospikeHandler.kt b/src/main/kotlin/com/aerospike/skyhook/handler/NettyAerospikeHandler.kt deleted file mode 100644 index 6e32acc..0000000 --- a/src/main/kotlin/com/aerospike/skyhook/handler/NettyAerospikeHandler.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.aerospike.skyhook.handler - -import com.aerospike.client.AerospikeException -import com.aerospike.client.IAerospikeClient -import com.aerospike.skyhook.command.RedisCommand -import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext -import com.aerospike.skyhook.config.ServerConfiguration -import com.aerospike.skyhook.handler.aerospike.DbsizeCommandHandler -import com.aerospike.skyhook.handler.aerospike.FlushCommandHandler -import com.aerospike.skyhook.handler.redis.* -import com.aerospike.skyhook.listener.key.* -import com.aerospike.skyhook.listener.list.* -import com.aerospike.skyhook.listener.map.* -import com.aerospike.skyhook.listener.scan.HscanCommandListener -import com.aerospike.skyhook.listener.scan.ScanCommandListener -import com.aerospike.skyhook.listener.scan.SscanCommandListener -import com.aerospike.skyhook.listener.scan.ZscanCommandListener -import io.netty.channel.ChannelHandlerContext -import mu.KotlinLogging -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class NettyAerospikeHandler @Inject constructor( - client: IAerospikeClient, - config: ServerConfiguration -) : NettyResponseWriter() { - - companion object { - private val log = KotlinLogging.logger(this::class.java.name) - } - - private val aerospikeCtx: AerospikeContext = AerospikeContext( - client, - config.namespase, - config.set, - config.bin, - config.typeBin - ) - - /** - * Handle the input command. Listeners are responsible to send the response - * to the client. - */ - fun handleCommand(cmd: RequestCommand, ctx: ChannelHandlerContext) { - try { - when (cmd.command) { - RedisCommand.GET -> GetCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.SET, - RedisCommand.SETNX, - RedisCommand.SETEX, - RedisCommand.PSETEX -> SetCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.MSET, - RedisCommand.MSETNX -> MsetCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.DEL, - RedisCommand.UNLINK -> DelCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.MGET -> MgetCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.GETSET -> GetsetCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.EXISTS, - RedisCommand.TOUCH -> ExistsCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.EXPIRE, - RedisCommand.PEXPIRE, - RedisCommand.EXPIREAT, - RedisCommand.PEXPIREAT, - RedisCommand.PERSIST -> ExpireCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.APPEND -> AppendCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.INCR, - RedisCommand.INCRBY, - RedisCommand.INCRBYFLOAT -> IncrCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.DECR, - RedisCommand.DECRBY -> DecrCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.STRLEN -> StrlenCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.TTL, - RedisCommand.PTTL -> TtlCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.RANDOMKEY -> RandomkeyCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.TYPE -> TypeCommandListener(aerospikeCtx, ctx).handle(cmd) - - RedisCommand.LPUSH, - RedisCommand.LPUSHX, - RedisCommand.RPUSH, - RedisCommand.RPUSHX -> ListPushCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.LINDEX -> LindexCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.LLEN -> LlenCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.LPOP, - RedisCommand.RPOP -> ListPopCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.LRANGE -> LrangeCommandListener(aerospikeCtx, ctx).handle(cmd) - - RedisCommand.HSETNX -> HsetnxCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.HSET -> HsetCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.HMSET -> HmsetCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.SADD -> SaddCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.HEXISTS, - RedisCommand.SISMEMBER -> HexistsCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.HGET, - RedisCommand.HMGET, - RedisCommand.HGETALL, - RedisCommand.HVALS, - RedisCommand.HKEYS, - RedisCommand.ZMSCORE, - RedisCommand.ZRANK, - RedisCommand.SMEMBERS -> MapGetCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.HINCRBY, - RedisCommand.HINCRBYFLOAT, - RedisCommand.ZINCRBY -> HincrbyCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.HSTRLEN -> HstrlenCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.HLEN, - RedisCommand.SCARD, - RedisCommand.ZCARD -> MapSizeCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.HDEL, - RedisCommand.SREM, - RedisCommand.ZREM -> MapDelCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.SUNION -> SunionCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.SINTER -> SinterCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.SUNIONSTORE -> SunionstoreCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.SINTERSTORE -> SinterstoreCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZADD -> ZaddCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZPOPMAX -> ZpopmaxCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZPOPMIN -> ZpopminCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZRANDMEMBER -> ZrandmemberCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZCOUNT -> ZcountCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZLEXCOUNT -> ZlexcountCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZREMRANGEBYSCORE -> ZremrangebyscoreCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZREMRANGEBYRANK -> ZremrangebyrankCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZREMRANGEBYLEX -> ZremrangebylexCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZRANGE -> ZrangeCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZRANGESTORE -> ZrangestoreCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZREVRANGE -> ZrevrangeCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZRANGEBYSCORE -> ZrangebyscoreCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZREVRANGEBYSCORE -> ZrevrangebyscoreCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZRANGEBYLEX -> ZrangebylexCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZREVRANGEBYLEX -> ZrevrangebylexCommandListener(aerospikeCtx, ctx).handle(cmd) - - RedisCommand.SCAN -> ScanCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.HSCAN -> HscanCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.SSCAN -> SscanCommandListener(aerospikeCtx, ctx).handle(cmd) - RedisCommand.ZSCAN -> ZscanCommandListener(aerospikeCtx, ctx).handle(cmd) - - RedisCommand.FLUSHDB, - RedisCommand.FLUSHALL -> FlushCommandHandler(aerospikeCtx, ctx).handle(cmd) - RedisCommand.DBSIZE -> DbsizeCommandHandler(aerospikeCtx, ctx).handle(cmd) - - RedisCommand.PING -> PingCommandHandler(ctx).handle(cmd) - RedisCommand.ECHO -> EchoCommandHandler(ctx).handle(cmd) - RedisCommand.LOLWUT -> LolwutCommandHandler(ctx).handle(cmd) - RedisCommand.TIME -> TimeCommandHandler(ctx).handle(cmd) - RedisCommand.QUIT, - RedisCommand.RESET, - RedisCommand.SAVE, - RedisCommand.BGSAVE -> MockCommandHandler(ctx).handle(cmd) - RedisCommand.COMMAND -> CommandCommandHandler(ctx).handle(cmd) - - else -> { - writeErrorString(ctx, "${cmd.command} unsupported command") - ctx.flush() - } - } - } catch (e: Exception) { - val msg = when (e) { - is AerospikeException -> "internal error" - else -> e.message - } - log.warn(e) {} - writeErrorString(ctx, msg) - ctx.flush() - } - } -} diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/NettyResponseWriter.kt b/src/main/kotlin/com/aerospike/skyhook/handler/NettyResponseWriter.kt index 1ec9a27..7d4b0f1 100644 --- a/src/main/kotlin/com/aerospike/skyhook/handler/NettyResponseWriter.kt +++ b/src/main/kotlin/com/aerospike/skyhook/handler/NettyResponseWriter.kt @@ -2,43 +2,48 @@ package com.aerospike.skyhook.handler import com.aerospike.client.Value import com.aerospike.client.Value.* +import com.aerospike.skyhook.listener.BaseListener +import com.aerospike.skyhook.pipeline.AerospikeChannelInitializer +import com.aerospike.skyhook.util.notify import io.netty.buffer.Unpooled import io.netty.channel.ChannelHandlerContext import io.netty.handler.codec.redis.* import java.io.IOException -open class NettyResponseWriter { +open class NettyResponseWriter( + protected val ctx: ChannelHandlerContext +) { companion object { private val pool: RedisMessagePool = FixedRedisMessagePool.INSTANCE } @Throws(IOException::class) - fun writeObject(ctx: ChannelHandlerContext, value: Any?) { + fun writeObject(value: Any?) { when (value) { null -> { - writeNullString(ctx) + writeNullString() } is List<*> -> { - writeObjectList(ctx, value as List<*>?) + writeObjectList(value as List<*>?) } is Set<*> -> { - writeObjectList(ctx, value.toList()) + writeObjectList(value.toList()) } is ByteArray -> { - writeByteArray(ctx, value as ByteArray?) + writeByteArray(value as ByteArray?) } is String -> { - writeBulkString(ctx, value as String?) + writeBulkString(value as String?) } is Long -> { - writeLong(ctx, value as Long?) + writeLong(value as Long?) } is Int -> { - writeLong(ctx, value as Int?) + writeLong(value as Int?) } is Double -> { - writeFloat(ctx, value as Double?) + writeFloat(value as Double?) } else -> { throw IllegalArgumentException("Unsupported value type") @@ -47,34 +52,34 @@ open class NettyResponseWriter { } @Throws(IOException::class) - fun writeObjectASBulkString(ctx: ChannelHandlerContext, value: Any?) { + fun writeObjectASBulkString(value: Any?) { when (value) { null -> { - writeNullString(ctx) + writeNullString() } is ByteArray -> { - writeByteArray(ctx, value as ByteArray?) + writeByteArray(value as ByteArray?) } is String -> { - writeBulkString(ctx, value as String?) + writeBulkString(value as String?) } is Long, is Int -> { - writeBulkString(ctx, value.toString()) + writeBulkString(value.toString()) } is Double -> { - writeBulkString(ctx, value.toString()) + writeBulkString(value.toString()) } is Value -> { - writeObjectASBulkString(ctx, value.getObject()) + writeObjectASBulkString(value.getObject()) } else -> { - writeNullString(ctx) + writeNullString() } } } @Throws(IOException::class) - fun writeArrayHeader(ctx: ChannelHandlerContext, length: Long) { + fun writeArrayHeader(length: Long) { ctx.write(ArrayHeaderRedisMessage(length)) } @@ -82,39 +87,39 @@ open class NettyResponseWriter { * Writes a zero length list: "*0\r\n". */ @Throws(IOException::class) - fun writeEmptyList(ctx: ChannelHandlerContext) { - writeArrayHeader(ctx, 0) + fun writeEmptyList() { + writeArrayHeader(0) } /** * Like WriteObjectList but every item is written as a bulkstring. */ @Throws(IOException::class) - fun writeObjectListStr(ctx: ChannelHandlerContext, objectCollection: Collection<*>?) { + fun writeObjectListStr(objectCollection: Collection<*>?) { if (objectCollection == null) { - writeNullArray(ctx) + writeNullArray() return } - writeArrayHeader(ctx, objectCollection.size.toLong()) + writeArrayHeader(objectCollection.size.toLong()) for (listItem in objectCollection) { - writeObjectASBulkString(ctx, listItem) + writeObjectASBulkString(listItem) } } @Throws(IOException::class) - fun writeObjectList(ctx: ChannelHandlerContext, objectList: List<*>?) { + fun writeObjectList(objectList: List<*>?) { if (objectList == null) { - writeNullArray(ctx) + writeNullArray() return } - writeArrayHeader(ctx, objectList.size.toLong()) + writeArrayHeader(objectList.size.toLong()) for (listItem in objectList) { - writeObject(ctx, listItem) + writeObject(listItem) } } @Throws(IOException::class) - fun writeBulkString(ctx: ChannelHandlerContext, stringValue: String?) { + fun writeBulkString(stringValue: String?) { if (stringValue == null) { ctx.write(FullBulkStringRedisMessage.NULL_INSTANCE) return @@ -125,32 +130,32 @@ open class NettyResponseWriter { } @Throws(IOException::class) - fun writeSimpleString(ctx: ChannelHandlerContext, sString: String?) { + fun writeSimpleString(sString: String?) { val simpleStringRedisMessage = pool.getSimpleString(sString) ?: SimpleStringRedisMessage(sString) ctx.write(simpleStringRedisMessage) } @Throws(IOException::class) - fun writeErrorString(ctx: ChannelHandlerContext, eString: String?) { + fun writeErrorString(eString: String?) { val errorRedisMessage = pool.getError(eString) ?: ErrorRedisMessage(eString) ctx.write(errorRedisMessage) } @Throws(IOException::class) - fun writeOK(ctx: ChannelHandlerContext) { - writeSimpleString(ctx, "OK") + fun writeOK() { + writeSimpleString("OK") } @Throws(IOException::class) - fun writeLongStr(ctx: ChannelHandlerContext, longValue: Long) { + fun writeLongStr(longValue: Long) { val longBytes = pool.getByteBufOfInteger(longValue) ?: String.format(":%d\r\n", longValue).toByteArray() ctx.write(FullBulkStringRedisMessage(Unpooled.wrappedBuffer(longBytes))) } @Throws(IOException::class) - fun writeFloat(ctx: ChannelHandlerContext, floatValue: Double?) { + fun writeFloat(floatValue: Double?) { val floatString = String.format("%f", floatValue) ctx.write( FullBulkStringRedisMessage( @@ -160,22 +165,22 @@ open class NettyResponseWriter { } @Throws(IOException::class) - fun writeByteArray(ctx: ChannelHandlerContext, asByteArray: ByteArray?) { + fun writeByteArray(asByteArray: ByteArray?) { ctx.write(FullBulkStringRedisMessage(Unpooled.wrappedBuffer(asByteArray))) } @Throws(IOException::class) - fun writeNullArray(ctx: ChannelHandlerContext) { + fun writeNullArray() { ctx.write(ArrayRedisMessage.NULL_INSTANCE) } @Throws(IOException::class) - fun writeNullString(ctx: ChannelHandlerContext) { + fun writeNullString() { ctx.write(FullBulkStringRedisMessage.NULL_INSTANCE) } @Throws(IOException::class) - fun writeBytesValue(ctx: ChannelHandlerContext, bytesValue: BytesValue) { + fun writeBytesValue(bytesValue: BytesValue) { ctx.write( FullBulkStringRedisMessage( Unpooled.wrappedBuffer(bytesValue.getObject() as ByteArray) @@ -184,7 +189,7 @@ open class NettyResponseWriter { } @Throws(IOException::class) - fun writeBulkStringValue(ctx: ChannelHandlerContext, inputString: StringValue?) { + fun writeBulkStringValue(inputString: StringValue?) { if (inputString == null) { ctx.write(FullBulkStringRedisMessage.NULL_INSTANCE) return @@ -199,7 +204,7 @@ open class NettyResponseWriter { } @Throws(IOException::class) - fun writeLong(ctx: ChannelHandlerContext, longValue: Long?) { + fun writeLong(longValue: Long?) { if (longValue == null) { ctx.write(FullBulkStringRedisMessage.NULL_INSTANCE) return @@ -210,18 +215,30 @@ open class NettyResponseWriter { } @Throws(IOException::class) - fun writeLong(ctx: ChannelHandlerContext, longValue: Int?) { - writeLong(ctx, longValue?.toLong()) + fun writeLong(longValue: Int?) { + writeLong(longValue?.toLong()) } @Throws(IOException::class) - fun writeLongValue(ctx: ChannelHandlerContext, longValue: LongValue) { + fun writeLongValue(longValue: LongValue) { val integerRedisMessage = pool.getInteger((longValue.getObject() as Long)) ?: IntegerRedisMessage((longValue.getObject() as Long)) ctx.write(integerRedisMessage) } - fun flush(ctx: ChannelHandlerContext) { + fun flushCtx() { ctx.flush() } + + fun flushCtxTransactionAware() { + if (ctx.channel().attr(AerospikeChannelInitializer.transactionAttrKey).get().inTransaction) { + synchronized(ctx) { ctx.notify() } + } + ctx.flush() + } + + fun closeCtx(e: Exception?) { + BaseListener.log.error(e) { "${this.javaClass.simpleName} error" } + ctx.close() + } } diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/aerospike/DbsizeCommandHandler.kt b/src/main/kotlin/com/aerospike/skyhook/handler/aerospike/DbsizeCommandHandler.kt index b5877a2..398bcc6 100644 --- a/src/main/kotlin/com/aerospike/skyhook/handler/aerospike/DbsizeCommandHandler.kt +++ b/src/main/kotlin/com/aerospike/skyhook/handler/aerospike/DbsizeCommandHandler.kt @@ -1,9 +1,7 @@ package com.aerospike.skyhook.handler.aerospike import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.handler.CommandHandler -import com.aerospike.skyhook.handler.NettyResponseWriter import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.InfoUtils.getNamespaceInfo import com.aerospike.skyhook.util.InfoUtils.getSetInfo @@ -11,23 +9,22 @@ import io.netty.channel.ChannelHandlerContext import kotlin.math.floor class DbsizeCommandHandler( - private val aeroCtx: AerospikeContext, - private val ctx: ChannelHandlerContext -) : NettyResponseWriter(), CommandHandler { + ctx: ChannelHandlerContext +) : BaseListener(ctx), CommandHandler { override fun handle(cmd: RequestCommand) { - require(cmd.argCount == 1) { BaseListener.argValidationErrorMsg(cmd) } + require(cmd.argCount == 1) { argValidationErrorMsg(cmd) } - writeLong(ctx, getTableRecordsNumber(aeroCtx.namespace, aeroCtx.set)) - ctx.flush() + writeLong(getTableRecordsNumber(aeroCtx.namespace, aeroCtx.set)) + flushCtxTransactionAware() } private fun getTableRecordsNumber(ns: String, set: String?): Long { - val allRecords = aeroCtx.client.nodes + val allRecords = client.nodes .map { getSetInfo(ns, set, it) } .map { it["objects"]!!.toInt() } .sum() - val replicationFactor = getNamespaceInfo(ns, aeroCtx.client.nodes[0])["effective_replication_factor"]!!.toInt() + val replicationFactor = getNamespaceInfo(ns, client.nodes[0])["effective_replication_factor"]!!.toInt() return floor(allRecords.toDouble() / replicationFactor).toLong() } } diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/aerospike/FlushCommandHandler.kt b/src/main/kotlin/com/aerospike/skyhook/handler/aerospike/FlushCommandHandler.kt index 3a0c002..78836a7 100644 --- a/src/main/kotlin/com/aerospike/skyhook/handler/aerospike/FlushCommandHandler.kt +++ b/src/main/kotlin/com/aerospike/skyhook/handler/aerospike/FlushCommandHandler.kt @@ -1,22 +1,19 @@ package com.aerospike.skyhook.handler.aerospike import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.handler.CommandHandler -import com.aerospike.skyhook.handler.NettyResponseWriter import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class FlushCommandHandler( - private val aeroCtx: AerospikeContext, - private val ctx: ChannelHandlerContext -) : NettyResponseWriter(), CommandHandler { + ctx: ChannelHandlerContext +) : BaseListener(ctx), CommandHandler { override fun handle(cmd: RequestCommand) { - require(cmd.argCount <= 2) { BaseListener.argValidationErrorMsg(cmd) } + require(cmd.argCount <= 2) { argValidationErrorMsg(cmd) } - aeroCtx.client.truncate(null, aeroCtx.namespace, aeroCtx.set, null) - writeOK(ctx) - ctx.flush() + client.truncate(null, aeroCtx.namespace, aeroCtx.set, null) + writeOK() + flushCtxTransactionAware() } } diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/aerospike/TransactionCommandsHandler.kt b/src/main/kotlin/com/aerospike/skyhook/handler/aerospike/TransactionCommandsHandler.kt new file mode 100644 index 0000000..1df6243 --- /dev/null +++ b/src/main/kotlin/com/aerospike/skyhook/handler/aerospike/TransactionCommandsHandler.kt @@ -0,0 +1,65 @@ +package com.aerospike.skyhook.handler.aerospike + +import com.aerospike.skyhook.command.RequestCommand +import com.aerospike.skyhook.handler.CommandHandler +import com.aerospike.skyhook.handler.NettyResponseWriter +import com.aerospike.skyhook.listener.BaseListener.Companion.argValidationErrorMsg +import com.aerospike.skyhook.pipeline.AerospikeChannelInitializer.Companion.transactionAttrKey +import com.aerospike.skyhook.util.wait +import io.netty.channel.ChannelHandlerContext + +class MultiCommandHandler( + ctx: ChannelHandlerContext, +) : NettyResponseWriter(ctx), CommandHandler { + + override fun handle(cmd: RequestCommand) { + require(cmd.argCount == 1) { argValidationErrorMsg(cmd) } + + ctx.channel().attr(transactionAttrKey).get().startTransaction() + writeOK() + flushCtx() + } +} + +class DiscardCommandHandler( + ctx: ChannelHandlerContext, +) : NettyResponseWriter(ctx), CommandHandler { + + override fun handle(cmd: RequestCommand) { + require(cmd.argCount == 1) { argValidationErrorMsg(cmd) } + + ctx.channel().attr(transactionAttrKey).get().clear() + writeOK() + flushCtx() + } +} + +class ExecCommandHandler( + ctx: ChannelHandlerContext, +) : NettyResponseWriter(ctx), CommandHandler { + + override fun handle(cmd: RequestCommand) { + require(cmd.argCount == 1) { argValidationErrorMsg(cmd) } + + val state = ctx.channel().attr(transactionAttrKey).get() + if (state.inTransaction) { + if (state.commands.isEmpty()) { + writeEmptyList() + } else { + try { + writeArrayHeader(state.commands.size.toLong()) + for (c in state.commands) { + state.pool.submit { c.command.newHandler(ctx).handle(c) } + synchronized(ctx) { ctx.wait(5000) } + } + } catch (e: Exception) { + writeErrorString("ERR Transaction failed") + } + state.clear() + } + } else { + writeErrorString("ERR EXEC without MULTI") + } + flushCtx() + } +} diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/redis/AuthCommandHandler.kt b/src/main/kotlin/com/aerospike/skyhook/handler/redis/AuthCommandHandler.kt new file mode 100644 index 0000000..6963196 --- /dev/null +++ b/src/main/kotlin/com/aerospike/skyhook/handler/redis/AuthCommandHandler.kt @@ -0,0 +1,32 @@ +package com.aerospike.skyhook.handler.redis + +import com.aerospike.skyhook.command.RequestCommand +import com.aerospike.skyhook.handler.CommandHandler +import com.aerospike.skyhook.handler.NettyResponseWriter +import com.aerospike.skyhook.listener.BaseListener +import com.aerospike.skyhook.pipeline.AerospikeChannelInitializer.Companion.authDetailsAttrKey +import com.aerospike.skyhook.pipeline.AerospikeChannelInitializer.Companion.clientPoolAttrKey +import com.aerospike.skyhook.util.client.AuthDetails +import io.netty.channel.ChannelHandlerContext + +class AuthCommandHandler( + ctx: ChannelHandlerContext, +) : NettyResponseWriter(ctx), CommandHandler { + + override fun handle(cmd: RequestCommand) { + require(cmd.argCount == 3) { BaseListener.argValidationErrorMsg(cmd) } + + val user = String(cmd.args[1]) + val password = String(cmd.args[2]) + val authDetails = AuthDetails(user, password) + + val client = ctx.channel().attr(clientPoolAttrKey).get().getClient(authDetails) + if (client != null) { + ctx.channel().attr(authDetailsAttrKey).set(authDetails.hashString) + writeOK() + } else { + writeErrorString("Invalid AUTH details") + } + flushCtxTransactionAware() + } +} diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/redis/CommandCommandHandler.kt b/src/main/kotlin/com/aerospike/skyhook/handler/redis/CommandCommandHandler.kt index 433c05a..4fab791 100644 --- a/src/main/kotlin/com/aerospike/skyhook/handler/redis/CommandCommandHandler.kt +++ b/src/main/kotlin/com/aerospike/skyhook/handler/redis/CommandCommandHandler.kt @@ -9,8 +9,8 @@ import io.netty.channel.ChannelHandlerContext import java.util.* class CommandCommandHandler( - private val ctx: ChannelHandlerContext -) : NettyResponseWriter(), CommandHandler { + ctx: ChannelHandlerContext +) : NettyResponseWriter(ctx), CommandHandler { override fun handle(cmd: RequestCommand) { require(cmd.argCount >= 1) { BaseListener.argValidationErrorMsg(cmd) } @@ -20,7 +20,7 @@ class CommandCommandHandler( } else { when (String(cmd.args[1]).toUpperCase(Locale.ENGLISH)) { "COUNT" -> { - writeLong(ctx, RedisCommand.totalCommands) + writeLong(RedisCommand.totalCommands) } "INFO" -> { val commands = cmd.args.drop(2).map { String(it) } @@ -32,6 +32,6 @@ class CommandCommandHandler( } } } - ctx.flush() + flushCtxTransactionAware() } } diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/redis/EchoCommandHandler.kt b/src/main/kotlin/com/aerospike/skyhook/handler/redis/EchoCommandHandler.kt index f363d48..96c6fcc 100644 --- a/src/main/kotlin/com/aerospike/skyhook/handler/redis/EchoCommandHandler.kt +++ b/src/main/kotlin/com/aerospike/skyhook/handler/redis/EchoCommandHandler.kt @@ -7,13 +7,13 @@ import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class EchoCommandHandler( - private val ctx: ChannelHandlerContext -) : NettyResponseWriter(), CommandHandler { + ctx: ChannelHandlerContext +) : NettyResponseWriter(ctx), CommandHandler { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 2) { BaseListener.argValidationErrorMsg(cmd) } - writeSimpleString(ctx, String(cmd.args[1])) - ctx.flush() + writeSimpleString(String(cmd.args[1])) + flushCtxTransactionAware() } } diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/redis/LolwutCommandHandler.kt b/src/main/kotlin/com/aerospike/skyhook/handler/redis/LolwutCommandHandler.kt index 4a994a7..387381e 100644 --- a/src/main/kotlin/com/aerospike/skyhook/handler/redis/LolwutCommandHandler.kt +++ b/src/main/kotlin/com/aerospike/skyhook/handler/redis/LolwutCommandHandler.kt @@ -8,13 +8,13 @@ import com.aerospike.skyhook.util.SystemUtils import io.netty.channel.ChannelHandlerContext class LolwutCommandHandler( - private val ctx: ChannelHandlerContext -) : NettyResponseWriter(), CommandHandler { + ctx: ChannelHandlerContext +) : NettyResponseWriter(ctx), CommandHandler { override fun handle(cmd: RequestCommand) { require(cmd.argCount <= 3) { BaseListener.argValidationErrorMsg(cmd) } - writeSimpleString(ctx, "Skyhook ver. ${SystemUtils.version}\n") - ctx.flush() + writeSimpleString("Skyhook ver. ${SystemUtils.version}\n") + flushCtxTransactionAware() } } diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/redis/MockCommandHandler.kt b/src/main/kotlin/com/aerospike/skyhook/handler/redis/MockCommandHandler.kt index 78228a3..827192b 100644 --- a/src/main/kotlin/com/aerospike/skyhook/handler/redis/MockCommandHandler.kt +++ b/src/main/kotlin/com/aerospike/skyhook/handler/redis/MockCommandHandler.kt @@ -8,16 +8,16 @@ import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class MockCommandHandler( - private val ctx: ChannelHandlerContext -) : NettyResponseWriter(), CommandHandler { + ctx: ChannelHandlerContext +) : NettyResponseWriter(ctx), CommandHandler { override fun handle(cmd: RequestCommand) { require(cmd.argCount >= 1) { BaseListener.argValidationErrorMsg(cmd) } when (cmd.command) { - RedisCommand.RESET -> writeSimpleString(ctx, "RESET") - else -> writeOK(ctx) + RedisCommand.RESET -> writeSimpleString("RESET") + else -> writeOK() } - ctx.flush() + flushCtxTransactionAware() } } diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/redis/PingCommandHandler.kt b/src/main/kotlin/com/aerospike/skyhook/handler/redis/PingCommandHandler.kt index 3a906ee..64f1f12 100644 --- a/src/main/kotlin/com/aerospike/skyhook/handler/redis/PingCommandHandler.kt +++ b/src/main/kotlin/com/aerospike/skyhook/handler/redis/PingCommandHandler.kt @@ -7,8 +7,8 @@ import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class PingCommandHandler( - private val ctx: ChannelHandlerContext -) : NettyResponseWriter(), CommandHandler { + ctx: ChannelHandlerContext +) : NettyResponseWriter(ctx), CommandHandler { override fun handle(cmd: RequestCommand) { require(cmd.argCount < 3) { BaseListener.argValidationErrorMsg(cmd) } @@ -19,7 +19,7 @@ class PingCommandHandler( "PONG" } - writeSimpleString(ctx, responseString) - ctx.flush() + writeSimpleString(responseString) + flushCtxTransactionAware() } } diff --git a/src/main/kotlin/com/aerospike/skyhook/handler/redis/TimeCommandHandler.kt b/src/main/kotlin/com/aerospike/skyhook/handler/redis/TimeCommandHandler.kt index 026b440..d14cddc 100644 --- a/src/main/kotlin/com/aerospike/skyhook/handler/redis/TimeCommandHandler.kt +++ b/src/main/kotlin/com/aerospike/skyhook/handler/redis/TimeCommandHandler.kt @@ -9,8 +9,8 @@ import java.time.Instant import java.util.concurrent.TimeUnit class TimeCommandHandler( - private val ctx: ChannelHandlerContext -) : NettyResponseWriter(), CommandHandler { + ctx: ChannelHandlerContext +) : NettyResponseWriter(ctx), CommandHandler { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 1) { BaseListener.argValidationErrorMsg(cmd) } @@ -19,7 +19,7 @@ class TimeCommandHandler( val seconds = now.epochSecond val microseconds = TimeUnit.NANOSECONDS.toMicros(now.nano.toLong()) - writeObjectListStr(ctx, arrayListOf(seconds, microseconds)) - ctx.flush() + writeObjectListStr(arrayListOf(seconds, microseconds)) + flushCtxTransactionAware() } } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/BaseListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/BaseListener.kt index 6b3cac9..8787e1e 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/BaseListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/BaseListener.kt @@ -7,14 +7,16 @@ import com.aerospike.skyhook.command.RequestCommand import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.handler.CommandHandler import com.aerospike.skyhook.handler.NettyResponseWriter +import com.aerospike.skyhook.pipeline.AerospikeChannelInitializer.Companion.aeroCtxAttrKey +import com.aerospike.skyhook.pipeline.AerospikeChannelInitializer.Companion.authDetailsAttrKey +import com.aerospike.skyhook.pipeline.AerospikeChannelInitializer.Companion.clientPoolAttrKey import io.netty.channel.ChannelHandlerContext import mu.KotlinLogging import java.io.IOException abstract class BaseListener( - protected val aeroCtx: AerospikeContext, - protected val ctx: ChannelHandlerContext -) : NettyResponseWriter(), CommandHandler { + ctx: ChannelHandlerContext +) : NettyResponseWriter(ctx), CommandHandler { companion object { @@ -79,24 +81,33 @@ abstract class BaseListener( return Operation.put(Bin(aeroCtx.typeBin, ValueType.STREAM.str)) } + protected val aeroCtx: AerospikeContext by lazy { + ctx.channel().attr(aeroCtxAttrKey).get() + } + + protected val client: IAerospikeClient by lazy { + ctx.channel().attr(clientPoolAttrKey).get().getClient( + ctx.channel().attr(authDetailsAttrKey).get() + ) + } + @Throws(IOException::class) protected open fun writeResponse(mapped: Any?) { - writeObject(ctx, mapped) + writeObject(mapped) } @Throws(IOException::class) protected open fun writeError(e: AerospikeException?) { - writeErrorString(ctx, "internal error") + writeErrorString("Internal error") } open fun onFailure(exception: AerospikeException?) { try { log.debug { exception } writeError(exception) - ctx.flush() + flushCtxTransactionAware() } catch (e: IOException) { - ctx.close() - log.error(e) { "Exception at onFailure" } + closeCtx(e) } } @@ -107,9 +118,4 @@ abstract class BaseListener( protected fun createKey(key: ByteArray): Key { return createKey(Value.get(String(key))) } - - protected fun closeCtx(e: Exception?) { - log.error(e) { "${this.javaClass.simpleName} error" } - ctx.close() - } } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/AppendCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/AppendCommandListener.kt index 36c8eec..d85d3cd 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/AppendCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/AppendCommandListener.kt @@ -3,14 +3,12 @@ package com.aerospike.skyhook.listener.key import com.aerospike.client.* import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class AppendCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 3) { argValidationErrorMsg(cmd) } @@ -21,18 +19,18 @@ class AppendCommandListener( Operation.append(Bin(aeroCtx.bin, Value.StringValue(String(cmd.args[2])))), Operation.get(aeroCtx.bin) ) - aeroCtx.client.operate(null, this, defaultWritePolicy, key, *ops) + client.operate(null, this, defaultWritePolicy, key, *ops) } override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeNullString(ctx) - ctx.flush() + writeNullString() + flushCtxTransactionAware() } else { try { val value: String = (record.bins[aeroCtx.bin] as String) - writeLong(ctx, value.length) - ctx.flush() + writeLong(value.length) + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/DelCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/DelCommandListener.kt index 4ff5147..ce2f087 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/DelCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/DelCommandListener.kt @@ -3,30 +3,28 @@ package com.aerospike.skyhook.listener.key import com.aerospike.client.Key import com.aerospike.client.listener.DeleteListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class DelCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), DeleteListener { +) : BaseListener(ctx), DeleteListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 2) { argValidationErrorMsg(cmd) } val key = createKey(cmd.key) - aeroCtx.client.delete(null, this, null, key) + client.delete(null, this, null, key) } override fun onSuccess(key: Key?, existed: Boolean) { try { if (existed) { - writeLong(ctx, 1L) + writeLong(1L) } else { - writeLong(ctx, 0L) + writeLong(0L) } - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/ExistsCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/ExistsCommandListener.kt index be89658..a188d77 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/ExistsCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/ExistsCommandListener.kt @@ -3,20 +3,18 @@ package com.aerospike.skyhook.listener.key import com.aerospike.client.Key import com.aerospike.client.listener.ExistsArrayListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class ExistsCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), ExistsArrayListener { +) : BaseListener(ctx), ExistsArrayListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount >= 2) { argValidationErrorMsg(cmd) } val keys = getKeys(cmd) - aeroCtx.client.exists( + client.exists( null, this, null, keys.toTypedArray() ) @@ -31,8 +29,8 @@ class ExistsCommandListener( override fun onSuccess(keys: Array?, exists: BooleanArray?) { try { val count = exists?.count { it } ?: 0 - writeLong(ctx, count) - ctx.flush() + writeLong(count) + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/ExpireCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/ExpireCommandListener.kt index 075732a..3c2cd78 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/ExpireCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/ExpireCommandListener.kt @@ -6,34 +6,32 @@ import com.aerospike.client.listener.WriteListener import com.aerospike.client.policy.WritePolicy import com.aerospike.skyhook.command.RedisCommand import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext import java.lang.Long.max class ExpireCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), WriteListener { +) : BaseListener(ctx), WriteListener { override fun handle(cmd: RequestCommand) { val key = createKey(cmd.key) val writePolicy = getPolicy(cmd) - aeroCtx.client.touch(null, this, writePolicy, key) + client.touch(null, this, writePolicy, key) } override fun onSuccess(key: Key?) { try { - writeLong(ctx, 1L) - ctx.flush() + writeLong(1L) + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } } override fun writeError(e: AerospikeException?) { - writeLong(ctx, 0L) + writeLong(0L) } private fun getPolicy(cmd: RequestCommand): WritePolicy { diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/GetCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/GetCommandListener.kt index 42e5e25..d8d947d 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/GetCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/GetCommandListener.kt @@ -4,30 +4,28 @@ import com.aerospike.client.Key import com.aerospike.client.Record import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class GetCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 2) { argValidationErrorMsg(cmd) } val key = createKey(cmd.key) - aeroCtx.client.get(null, this, null, key) + client.get(null, this, null, key) } override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeNullString(ctx) - ctx.flush() + writeNullString() + flushCtxTransactionAware() } else { try { writeResponse(record.bins[aeroCtx.bin]) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/GetsetCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/GetsetCommandListener.kt index 82e9d5d..f185833 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/GetsetCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/GetsetCommandListener.kt @@ -3,15 +3,13 @@ package com.aerospike.skyhook.listener.key import com.aerospike.client.* import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class GetsetCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 3) { argValidationErrorMsg(cmd) } @@ -23,21 +21,21 @@ class GetsetCommandListener( Operation.put(Bin(aeroCtx.bin, value)) ) - aeroCtx.client.operate(null, this, updateOnlyPolicy, key, *ops) + client.operate(null, this, updateOnlyPolicy, key, *ops) } override fun writeError(e: AerospikeException?) { - writeNullString(ctx) + writeNullString() } override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeNullString(ctx) - ctx.flush() + writeNullString() + flushCtxTransactionAware() } else { try { writeResponse(record.bins[aeroCtx.bin]) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/MgetCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/MgetCommandListener.kt index 7d19d8e..3c28e7d 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/MgetCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/MgetCommandListener.kt @@ -3,14 +3,12 @@ package com.aerospike.skyhook.listener.key import com.aerospike.client.BatchRead import com.aerospike.client.listener.BatchListListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class MgetCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), BatchListListener { +) : BaseListener(ctx), BatchListListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount >= 2) { argValidationErrorMsg(cmd) } @@ -18,21 +16,21 @@ class MgetCommandListener( val keys = cmd.args.drop(1) .map { createKey(it) } .map { BatchRead(it, true) } - aeroCtx.client.get(null, this, null, keys) + client.get(null, this, null, keys) } override fun onSuccess(records: MutableList?) { if (records == null) { - writeNullString(ctx) - ctx.flush() + writeNullString() + flushCtxTransactionAware() } else { try { - writeObjectListStr(ctx, records.mapNotNull { + writeObjectListStr(records.mapNotNull { if (it.record != null) { it.record.bins[aeroCtx.bin] } else null }) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/MsetCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/MsetCommandListener.kt index dfccb7a..b96c363 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/MsetCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/MsetCommandListener.kt @@ -6,15 +6,13 @@ import com.aerospike.client.Value import com.aerospike.client.listener.WriteListener import com.aerospike.skyhook.command.RedisCommand import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class MsetCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), WriteListener { +) : BaseListener(ctx), WriteListener { @Volatile private lateinit var command: RedisCommand @@ -38,7 +36,7 @@ class MsetCommandListener( total = values.size values.forEach { (k, v) -> - aeroCtx.client.put( + client.put( null, this, defaultWritePolicy, k, Bin(aeroCtx.bin, v), stringTypeBin() ) @@ -47,9 +45,9 @@ class MsetCommandListener( private fun handleNX(cmd: RequestCommand, keys: Array): Boolean { if (cmd.command == RedisCommand.MSETNX) { - if (!aeroCtx.client.exists(null, keys).all { !it }) { - writeLong(ctx, 0L) - ctx.flush() + if (!client.exists(null, keys).all { !it }) { + writeLong(0L) + flushCtxTransactionAware() return false } } @@ -68,11 +66,11 @@ class MsetCommandListener( total-- if (total == 0) { if (command == RedisCommand.MSETNX) { - writeLong(ctx, 1L) + writeLong(1L) } else { - writeOK(ctx) + writeOK() } - ctx.flush() + flushCtxTransactionAware() } } } catch (e: Exception) { diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/RandomkeyCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/RandomkeyCommandListener.kt index 9b79d3a..f86a1b1 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/RandomkeyCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/RandomkeyCommandListener.kt @@ -5,14 +5,12 @@ import com.aerospike.client.Record import com.aerospike.client.listener.RecordSequenceListener import com.aerospike.client.policy.ScanPolicy import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class RandomkeyCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordSequenceListener { +) : BaseListener(ctx), RecordSequenceListener { private var isEmpty: Boolean = true @@ -23,7 +21,7 @@ class RandomkeyCommandListener( scanPolicy.maxRecords = 1 scanPolicy.includeBinData = false - aeroCtx.client.scanAll( + client.scanAll( null, this, scanPolicy, aeroCtx.namespace, aeroCtx.set ) @@ -37,7 +35,7 @@ class RandomkeyCommandListener( } override fun onSuccess() { - if (isEmpty) writeNullString(ctx) - ctx.flush() + if (isEmpty) writeNullString() + flushCtxTransactionAware() } } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/SetCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/SetCommandListener.kt index 48be2c2..1a81e3f 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/SetCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/SetCommandListener.kt @@ -9,15 +9,13 @@ import com.aerospike.client.policy.RecordExistsAction import com.aerospike.client.policy.WritePolicy import com.aerospike.skyhook.command.RedisCommand import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class SetCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), WriteListener { +) : BaseListener(ctx), WriteListener { @Volatile private lateinit var command: RedisCommand @@ -28,7 +26,7 @@ class SetCommandListener( command = cmd.command val key = createKey(cmd.key) val params = parse(cmd) - aeroCtx.client.put( + client.put( null, this, params.writePolicy, key, Bin(aeroCtx.bin, params.value), stringTypeBin() ) @@ -37,11 +35,11 @@ class SetCommandListener( override fun onSuccess(key: Key?) { try { if (command == RedisCommand.SETNX) { - writeLong(ctx, 1L) + writeLong(1L) } else { - writeOK(ctx) + writeOK() } - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } @@ -49,9 +47,9 @@ class SetCommandListener( override fun writeError(e: AerospikeException?) { if (command == RedisCommand.SETNX) { - writeLong(ctx, 0L) + writeLong(0L) } else { - writeErrorString(ctx, "internal error") + writeErrorString("Internal error") } } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/StrlenCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/StrlenCommandListener.kt index 466b5c8..1af11c5 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/StrlenCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/StrlenCommandListener.kt @@ -4,30 +4,28 @@ import com.aerospike.client.Key import com.aerospike.client.Record import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class StrlenCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 2) { argValidationErrorMsg(cmd) } val key = createKey(cmd.key) - aeroCtx.client.get(null, this, null, key) + client.get(null, this, null, key) } override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeLong(ctx, 0L) - ctx.flush() + writeLong(0L) + flushCtxTransactionAware() } else { try { writeResponse(getValueStrLen(record.bins[aeroCtx.bin])) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/TtlCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/TtlCommandListener.kt index f707839..b933689 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/TtlCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/TtlCommandListener.kt @@ -5,14 +5,12 @@ import com.aerospike.client.Record import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RedisCommand import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class TtlCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { @Volatile private var m: Long = 1L @@ -22,18 +20,18 @@ class TtlCommandListener( val key = createKey(cmd.key) if (cmd.command == RedisCommand.PTTL) m = 1000L - aeroCtx.client.getHeader(null, this, null, key) + client.getHeader(null, this, null, key) } override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeLong(ctx, -2L) - ctx.flush() + writeLong(-2L) + flushCtxTransactionAware() } else { try { val ttl = if (record.timeToLive == -1) -1L else record.timeToLive * m - writeLong(ctx, ttl) - ctx.flush() + writeLong(ttl) + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/TypeCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/TypeCommandListener.kt index 10bae9b..98c32a7 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/TypeCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/TypeCommandListener.kt @@ -4,20 +4,18 @@ import com.aerospike.client.Key import com.aerospike.client.Record import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class TypeCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 2) { argValidationErrorMsg(cmd) } val key = createKey(cmd.key) - aeroCtx.client.get( + client.get( null, this, null, key, aeroCtx.typeBin ) @@ -25,12 +23,12 @@ class TypeCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeNullString(ctx) - ctx.flush() + writeNullString() + flushCtxTransactionAware() } else { try { writeResponse(record.bins[aeroCtx.typeBin]) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/key/UnaryCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/key/UnaryCommandListener.kt index 55a1860..c55a66b 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/key/UnaryCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/key/UnaryCommandListener.kt @@ -7,15 +7,13 @@ import com.aerospike.client.Record import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RedisCommand import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext abstract class UnaryCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 2 || cmd.argCount == 3) { @@ -28,7 +26,7 @@ abstract class UnaryCommandListener( getUnaryOperation(cmd), Operation.get(aeroCtx.bin) ) - aeroCtx.client.operate( + client.operate( null, this, defaultWritePolicy, key, *ops ) @@ -36,12 +34,12 @@ abstract class UnaryCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeNullString(ctx) - ctx.flush() + writeNullString() + flushCtxTransactionAware() } else { try { - writeObject(ctx, record.bins[aeroCtx.bin]) - ctx.flush() + writeObject(record.bins[aeroCtx.bin]) + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } @@ -52,9 +50,8 @@ abstract class UnaryCommandListener( } class IncrCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : UnaryCommandListener(aeroCtx, ctx) { +) : UnaryCommandListener(ctx) { override fun getUnaryOperation(cmd: RequestCommand): Operation { return when (cmd.command) { @@ -75,9 +72,8 @@ class IncrCommandListener( } class DecrCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : UnaryCommandListener(aeroCtx, ctx) { +) : UnaryCommandListener(ctx) { override fun getUnaryOperation(cmd: RequestCommand): Operation { return when (cmd.command) { diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/list/LindexCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/list/LindexCommandListener.kt index c02c0aa..46f57fb 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/list/LindexCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/list/LindexCommandListener.kt @@ -6,15 +6,13 @@ import com.aerospike.client.cdt.ListOperation import com.aerospike.client.cdt.ListReturnType import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class LindexCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 3) { argValidationErrorMsg(cmd) } @@ -26,7 +24,7 @@ class LindexCommandListener( aeroCtx.bin, index, ListReturnType.VALUE ) - aeroCtx.client.operate( + client.operate( null, this, defaultWritePolicy, key, operation ) @@ -34,12 +32,12 @@ class LindexCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeEmptyList(ctx) - ctx.flush() + writeEmptyList() + flushCtxTransactionAware() } else { try { writeResponse(record.bins[aeroCtx.bin]) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/list/ListPopCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/list/ListPopCommandListener.kt index a3bdb26..db4295e 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/list/ListPopCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/list/ListPopCommandListener.kt @@ -7,15 +7,13 @@ import com.aerospike.client.cdt.ListOperation import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RedisCommand import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class ListPopCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 2 || cmd.argCount == 3) { @@ -23,7 +21,7 @@ class ListPopCommandListener( } val key = createKey(cmd.key) - aeroCtx.client.operate( + client.operate( null, this, defaultWritePolicy, key, getListOperation(cmd) ) @@ -46,12 +44,12 @@ class ListPopCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeNullArray(ctx) - ctx.flush() + writeNullArray() + flushCtxTransactionAware() } else { try { writeResponse(record.bins[aeroCtx.bin]) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/list/ListPushCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/list/ListPushCommandListener.kt index 1795186..84a358e 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/list/ListPushCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/list/ListPushCommandListener.kt @@ -9,15 +9,13 @@ import com.aerospike.client.listener.RecordListener import com.aerospike.client.policy.WritePolicy import com.aerospike.skyhook.command.RedisCommand import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class ListPushCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { private data class OpWritePolicy(val writePolicy: WritePolicy, val op: Operation) @@ -26,7 +24,7 @@ class ListPushCommandListener( val key = createKey(cmd.key) val opPolicy = getOpWritePolicy(cmd) - aeroCtx.client.operate( + client.operate( null, this, opPolicy.writePolicy, key, listTypeOp(), opPolicy.op ) @@ -73,12 +71,12 @@ class ListPushCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeNullArray(ctx) - ctx.flush() + writeNullArray() + flushCtxTransactionAware() } else { try { writeResponse(record.bins[aeroCtx.bin]) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/list/LlenCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/list/LlenCommandListener.kt index 11355a4..e82bab6 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/list/LlenCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/list/LlenCommandListener.kt @@ -5,21 +5,19 @@ import com.aerospike.client.Record import com.aerospike.client.cdt.ListOperation import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class LlenCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 2) { argValidationErrorMsg(cmd) } val key = createKey(cmd.key) val operation = ListOperation.size(aeroCtx.bin) - aeroCtx.client.operate( + client.operate( null, this, null, key, operation ) @@ -27,12 +25,12 @@ class LlenCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeEmptyList(ctx) - ctx.flush() + writeEmptyList() + flushCtxTransactionAware() } else { try { writeResponse(record.bins[aeroCtx.bin]) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/list/LrangeCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/list/LrangeCommandListener.kt index 9192fdb..e0613e1 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/list/LrangeCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/list/LrangeCommandListener.kt @@ -6,15 +6,13 @@ import com.aerospike.client.cdt.ListOperation import com.aerospike.client.cdt.ListReturnType import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class LrangeCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 4) { argValidationErrorMsg(cmd) } @@ -32,7 +30,7 @@ class LrangeCommandListener( aeroCtx.bin, from, count, ListReturnType.VALUE ) - aeroCtx.client.operate( + client.operate( null, this, defaultWritePolicy, key, operation ) @@ -40,12 +38,12 @@ class LrangeCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeEmptyList(ctx) - ctx.flush() + writeEmptyList() + flushCtxTransactionAware() } else { try { writeResponse(record.bins[aeroCtx.bin]) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/HexistsCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/HexistsCommandListener.kt index 9b01ffb..8e7b3ae 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/HexistsCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/HexistsCommandListener.kt @@ -6,15 +6,13 @@ import com.aerospike.client.cdt.MapOperation import com.aerospike.client.cdt.MapReturnType import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class HexistsCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount >= 3) { argValidationErrorMsg(cmd) } @@ -24,7 +22,7 @@ class HexistsCommandListener( aeroCtx.bin, Typed.getValue(cmd.args[2]), MapReturnType.COUNT ) - aeroCtx.client.operate( + client.operate( null, this, defaultWritePolicy, key, operation ) @@ -32,12 +30,12 @@ class HexistsCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeNullString(ctx) - ctx.flush() + writeNullString() + flushCtxTransactionAware() } else { try { writeResponse(record.bins[aeroCtx.bin]) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/HincrbyCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/HincrbyCommandListener.kt index 7d30745..8462132 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/HincrbyCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/HincrbyCommandListener.kt @@ -8,15 +8,13 @@ import com.aerospike.client.cdt.MapPolicy import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RedisCommand import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class HincrbyCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { @Volatile private lateinit var command: RedisCommand @@ -30,7 +28,7 @@ class HincrbyCommandListener( MapPolicy(), aeroCtx.bin, getMapKey(cmd), getIncrValue(cmd) ) - aeroCtx.client.operate( + client.operate( null, this, defaultWritePolicy, key, operation ) @@ -52,15 +50,15 @@ class HincrbyCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeErrorString(ctx, "failed to create a record") - ctx.flush() + writeErrorString("Failed to create a record") + flushCtxTransactionAware() } else { try { when (command) { RedisCommand.ZINCRBY -> writeResponse(record.getLong(aeroCtx.bin).toString()) else -> writeResponse(record.bins[aeroCtx.bin]) } - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/HsetCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/HsetCommandListener.kt index 35e9e12..5276809 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/HsetCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/HsetCommandListener.kt @@ -7,15 +7,13 @@ import com.aerospike.client.cdt.MapPolicy import com.aerospike.client.cdt.MapWriteFlags import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class HsetnxCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 4) { argValidationErrorMsg(cmd) } @@ -27,24 +25,24 @@ class HsetnxCommandListener( Typed.getValue(cmd.args[2]), Typed.getValue(cmd.args[3]) ) - aeroCtx.client.operate( + client.operate( null, this, defaultWritePolicy, key, hashTypeOp(), operation ) } override fun writeError(e: AerospikeException?) { - writeLong(ctx, 0L) + writeLong(0L) } override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeNullString(ctx) - ctx.flush() + writeNullString() + flushCtxTransactionAware() } else { try { - writeLong(ctx, 1L) - ctx.flush() + writeLong(1L) + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } @@ -53,9 +51,8 @@ class HsetnxCommandListener( } open class HsetCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : SaddCommandListener(aeroCtx, ctx) { +) : SaddCommandListener(ctx) { override val typeOperation: Operation = hashTypeOp() override val mapPolicy = MapPolicy() @@ -74,20 +71,19 @@ open class HsetCommandListener( } class HmsetCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : HsetCommandListener(aeroCtx, ctx) { +) : HsetCommandListener(ctx) { override fun setSize(key: Key) {} override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeNullString(ctx) - ctx.flush() + writeNullString() + flushCtxTransactionAware() } else { try { - writeOK(ctx) - ctx.flush() + writeOK() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/HstrlenCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/HstrlenCommandListener.kt index 3d05a7e..90ab052 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/HstrlenCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/HstrlenCommandListener.kt @@ -7,15 +7,13 @@ import com.aerospike.client.cdt.MapOperation import com.aerospike.client.cdt.MapReturnType import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class HstrlenCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 3) { argValidationErrorMsg(cmd) } @@ -26,19 +24,19 @@ class HstrlenCommandListener( aeroCtx.bin, mapKey, MapReturnType.VALUE ) - aeroCtx.client.operate( + client.operate( null, this, null, key, operation ) } override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeLong(ctx, 0L) - ctx.flush() + writeLong(0L) + flushCtxTransactionAware() } else { try { writeResponse(getValueStrLen(record.bins[aeroCtx.bin])) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/MapDelCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/MapDelCommandListener.kt index 9e40721..7b4c063 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/MapDelCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/MapDelCommandListener.kt @@ -7,15 +7,13 @@ import com.aerospike.client.cdt.MapOperation import com.aerospike.client.cdt.MapReturnType import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class MapDelCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount >= 3) { argValidationErrorMsg(cmd) } @@ -25,7 +23,7 @@ class MapDelCommandListener( aeroCtx.bin, getValues(cmd), MapReturnType.COUNT ) - aeroCtx.client.operate( + client.operate( null, this, null, key, operation ) } @@ -37,12 +35,12 @@ class MapDelCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeNullArray(ctx) - ctx.flush() + writeNullArray() + flushCtxTransactionAware() } else { try { writeResponse(record.bins[aeroCtx.bin]) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/MapGetCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/MapGetCommandListener.kt index 6537261..2cfd1db 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/MapGetCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/MapGetCommandListener.kt @@ -9,15 +9,13 @@ import com.aerospike.client.cdt.MapReturnType import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RedisCommand import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class MapGetCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { @Volatile private lateinit var command: RedisCommand @@ -26,7 +24,7 @@ class MapGetCommandListener( command = cmd.command val key = createKey(cmd.key) - aeroCtx.client.operate( + client.operate( null, this, null, key, getOperation(cmd) ) @@ -99,11 +97,11 @@ class MapGetCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { writeNull() - ctx.flush() + flushCtxTransactionAware() } else { try { writeResponse(marshalOutput(record.bins[aeroCtx.bin])) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } @@ -116,8 +114,8 @@ class MapGetCommandListener( RedisCommand.HVALS, RedisCommand.HKEYS, RedisCommand.SMEMBERS -> - writeEmptyList(ctx) - else -> writeNullString(ctx) + writeEmptyList() + else -> writeNullString() } } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/MapSizeCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/MapSizeCommandListener.kt index f11ad42..53d4a44 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/MapSizeCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/MapSizeCommandListener.kt @@ -5,14 +5,12 @@ import com.aerospike.client.Record import com.aerospike.client.cdt.MapOperation import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext class MapSizeCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 2) { argValidationErrorMsg(cmd) } @@ -20,7 +18,7 @@ class MapSizeCommandListener( val key = createKey(cmd.key) val operation = MapOperation.size(aeroCtx.bin) - aeroCtx.client.operate( + client.operate( null, this, null, key, operation ) @@ -28,12 +26,12 @@ class MapSizeCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeLong(ctx, 0L) - ctx.flush() + writeLong(0L) + flushCtxTransactionAware() } else { try { writeResponse(record.bins[aeroCtx.bin]) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/SaddCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/SaddCommandListener.kt index b0f1a48..b9c06b7 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/SaddCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/SaddCommandListener.kt @@ -7,15 +7,13 @@ import com.aerospike.client.cdt.MapPolicy import com.aerospike.client.cdt.MapWriteFlags import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext open class SaddCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { @Volatile protected open var size: Long = 0L @@ -33,7 +31,7 @@ open class SaddCommandListener( aeroCtx.bin, getValues(cmd) ) - aeroCtx.client.operate( + client.operate( null, this, defaultWritePolicy, key, typeOperation, operation ) @@ -45,7 +43,7 @@ open class SaddCommandListener( protected open fun setSize(key: Key) { val getSize = MapOperation.size(aeroCtx.bin) - size = aeroCtx.client.operate(defaultWritePolicy, key, getSize) + size = client.operate(defaultWritePolicy, key, getSize) ?.getLong(aeroCtx.bin) ?: 0L } @@ -56,18 +54,18 @@ open class SaddCommandListener( } override fun writeError(e: AerospikeException?) { - writeLong(ctx, 0L) + writeLong(0L) } override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeLong(ctx, 0L) - ctx.flush() + writeLong(0L) + flushCtxTransactionAware() } else { try { val added = record.getLong(aeroCtx.bin) - size - writeLong(ctx, added) - ctx.flush() + writeLong(added) + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/SmergeCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/SmergeCommandListener.kt index 1d54fee..6541841 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/SmergeCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/SmergeCommandListener.kt @@ -4,7 +4,6 @@ import com.aerospike.client.Key import com.aerospike.client.Record import com.aerospike.client.listener.RecordArrayListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.IntersectMerge import com.aerospike.skyhook.util.Merge @@ -12,14 +11,13 @@ import com.aerospike.skyhook.util.UnionMerge import io.netty.channel.ChannelHandlerContext abstract class SmergeBaseCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordArrayListener, Merge { +) : BaseListener(ctx), RecordArrayListener, Merge { override fun handle(cmd: RequestCommand) { require(cmd.argCount >= 2) { argValidationErrorMsg(cmd) } - aeroCtx.client.get( + client.get( null, this, null, getKeys(cmd).toTypedArray() ) @@ -32,22 +30,20 @@ abstract class SmergeBaseCommandListener( override fun onSuccess(keys: Array?, records: Array?) { if (records == null) { - writeEmptyList(ctx) + writeEmptyList() } else { val values = merge(records.filterNotNull() .map { it.getMap(aeroCtx.bin) }.map { it.keys }) writeResponse(values) } - ctx.flush() + flushCtxTransactionAware() } } class SinterCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : SmergeBaseCommandListener(aeroCtx, ctx), IntersectMerge +) : SmergeBaseCommandListener(ctx), IntersectMerge class SunionCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : SmergeBaseCommandListener(aeroCtx, ctx), UnionMerge +) : SmergeBaseCommandListener(ctx), UnionMerge diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/SstoreCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/SstoreCommandListener.kt index 7937523..70589e5 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/SstoreCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/SstoreCommandListener.kt @@ -7,7 +7,6 @@ import com.aerospike.client.cdt.MapOperation import com.aerospike.client.cdt.MapPolicy import com.aerospike.client.listener.RecordArrayListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.IntersectMerge import com.aerospike.skyhook.util.Merge @@ -16,9 +15,8 @@ import com.aerospike.skyhook.util.UnionMerge import io.netty.channel.ChannelHandlerContext abstract class SstoreBaseCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordArrayListener, Merge { +) : BaseListener(ctx), RecordArrayListener, Merge { @Volatile private lateinit var key: Key @@ -27,7 +25,7 @@ abstract class SstoreBaseCommandListener( require(cmd.argCount >= 3) { argValidationErrorMsg(cmd) } key = createKey(cmd.key) - aeroCtx.client.get( + client.get( null, this, null, getKeys(cmd).toTypedArray() ) @@ -40,7 +38,7 @@ abstract class SstoreBaseCommandListener( override fun onSuccess(keys: Array?, records: Array?) { if (records == null) { - writeLong(ctx, 0L) + writeLong(0L) } else { val values = merge(records.filterNotNull() .map { it.getMap(aeroCtx.bin) }.map { it.keys }) @@ -52,21 +50,19 @@ abstract class SstoreBaseCommandListener( Typed.getValue(it.toString().toByteArray()) to Value.getAsNull() }.toMap() ) - aeroCtx.client.operate( + client.operate( defaultWritePolicy, key, setTypeOp(), operation ) - writeLong(ctx, values.size) + writeLong(values.size) } - ctx.flush() + flushCtxTransactionAware() } } class SinterstoreCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : SstoreBaseCommandListener(aeroCtx, ctx), IntersectMerge +) : SstoreBaseCommandListener(ctx), IntersectMerge class SunionstoreCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : SstoreBaseCommandListener(aeroCtx, ctx), UnionMerge +) : SstoreBaseCommandListener(ctx), UnionMerge diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/ZaddCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/ZaddCommandListener.kt index e1d606c..c426e65 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/ZaddCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/ZaddCommandListener.kt @@ -10,15 +10,13 @@ import com.aerospike.client.cdt.MapPolicy import com.aerospike.client.cdt.MapWriteFlags import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class ZaddCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { private class ZaddCommand(val cmd: RequestCommand) { var XX: Boolean = false @@ -91,10 +89,10 @@ class ZaddCommandListener( zaddCommand = ZaddCommand(cmd) val getSize = MapOperation.size(aeroCtx.bin) - size = aeroCtx.client.operate(defaultWritePolicy, key, getSize) + size = client.operate(defaultWritePolicy, key, getSize) ?.getLong(aeroCtx.bin) ?: 0L - aeroCtx.client.operate( + client.operate( null, this, defaultWritePolicy, key, zsetTypeOp(), getMapOperation() ) @@ -138,17 +136,17 @@ class ZaddCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeLong(ctx, 0L) - ctx.flush() + writeLong(0L) + flushCtxTransactionAware() } else { try { if (zaddCommand.INCR) { writeResponse(record.getString(aeroCtx.bin)) } else { val added = record.getLong(aeroCtx.bin) - size - writeLong(ctx, added) + writeLong(added) } - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/ZcountCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/ZcountCommandListener.kt index e81baff..353fe6f 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/ZcountCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/ZcountCommandListener.kt @@ -5,22 +5,20 @@ import com.aerospike.client.cdt.MapOperation import com.aerospike.client.cdt.MapReturnType import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Intervals import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext open class ZcountCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { override fun handle(cmd: RequestCommand) { require(cmd.argCount == 4) { argValidationErrorMsg(cmd) } val key = createKey(cmd.key) - aeroCtx.client.operate( + client.operate( null, this, null, key, getOperation(cmd) ) @@ -36,17 +34,17 @@ open class ZcountCommandListener( } override fun writeError(e: AerospikeException?) { - writeLong(ctx, 0L) + writeLong(0L) } override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeLong(ctx, 0L) - ctx.flush() + writeLong(0L) + flushCtxTransactionAware() } else { try { - writeLong(ctx, record.getLong(aeroCtx.bin)) - ctx.flush() + writeLong(record.getLong(aeroCtx.bin)) + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } @@ -55,9 +53,8 @@ open class ZcountCommandListener( } class ZlexcountCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : ZcountCommandListener(aeroCtx, ctx) { +) : ZcountCommandListener(ctx) { override fun getOperation(cmd: RequestCommand): Operation { return MapOperation.getByKeyRange( @@ -70,9 +67,8 @@ class ZlexcountCommandListener( } class ZremrangebyscoreCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : ZcountCommandListener(aeroCtx, ctx) { +) : ZcountCommandListener(ctx) { override fun getOperation(cmd: RequestCommand): Operation { return MapOperation.removeByValueRange( @@ -85,9 +81,8 @@ class ZremrangebyscoreCommandListener( } class ZremrangebyrankCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : ZcountCommandListener(aeroCtx, ctx) { +) : ZcountCommandListener(ctx) { override fun getOperation(cmd: RequestCommand): Operation { val from = Typed.getInteger(cmd.args[2]) @@ -105,7 +100,7 @@ class ZremrangebyrankCommandListener( return maxOf( if (to < 0) { val key = createKey(cmd.key) - val mapSize = aeroCtx.client.operate( + val mapSize = client.operate( null, key, MapOperation.size(aeroCtx.bin) ).getInt(aeroCtx.bin) @@ -118,9 +113,8 @@ class ZremrangebyrankCommandListener( } class ZremrangebylexCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : ZcountCommandListener(aeroCtx, ctx) { +) : ZcountCommandListener(ctx) { override fun getOperation(cmd: RequestCommand): Operation { return MapOperation.removeByKeyRange( diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/ZpopCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/ZpopCommandListener.kt index 064de05..c494868 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/ZpopCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/ZpopCommandListener.kt @@ -7,15 +7,13 @@ import com.aerospike.client.cdt.MapOperation import com.aerospike.client.cdt.MapReturnType import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext import java.util.* abstract class ZpopCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { protected var count: Int = 0 @@ -26,9 +24,9 @@ abstract class ZpopCommandListener( val key = createKey(cmd.key) setCount(cmd) - val record = aeroCtx.client.get(defaultWritePolicy, key).bins[aeroCtx.bin] + val record = client.get(defaultWritePolicy, key).bins[aeroCtx.bin] - aeroCtx.client.operate( + client.operate( null, this, defaultWritePolicy, key, MapOperation.removeByKeyList(aeroCtx.bin, getKeysToPop(record), MapReturnType.KEY_VALUE) ) @@ -49,12 +47,12 @@ abstract class ZpopCommandListener( override fun onSuccess(key: Key?, record: Record?) { if (record == null) { - writeEmptyList(ctx) - ctx.flush() + writeEmptyList() + flushCtxTransactionAware() } else { try { writeResponse(marshalOutput(record.bins[aeroCtx.bin])) - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } @@ -71,9 +69,8 @@ abstract class ZpopCommandListener( } class ZpopmaxCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : ZpopCommandListener(aeroCtx, ctx) { +) : ZpopCommandListener(ctx) { override fun take(sorted: List): List { return sorted.takeLast(count) @@ -85,9 +82,8 @@ class ZpopmaxCommandListener( } class ZpopminCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : ZpopCommandListener(aeroCtx, ctx) { +) : ZpopCommandListener(ctx) { override fun take(sorted: List): List { return sorted.take(count) diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/ZrandmemberCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/ZrandmemberCommandListener.kt index 681ce25..0708961 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/ZrandmemberCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/ZrandmemberCommandListener.kt @@ -7,15 +7,13 @@ import com.aerospike.client.cdt.MapOperation import com.aerospike.client.cdt.MapReturnType import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext import java.util.concurrent.atomic.AtomicInteger class ZrandmemberCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { @Volatile private lateinit var zrandmemberCommand: ZrandmemberCommand @@ -67,10 +65,10 @@ class ZrandmemberCommandListener( zrandmemberCommand.set(indexList.size) if (zrandmemberCommand.get() > 1) { - writeArrayHeader(ctx, zrandmemberCommand.get().toLong()) + writeArrayHeader(zrandmemberCommand.get().toLong()) } for (i: Int in indexList) { - aeroCtx.client.operate( + client.operate( null, this, defaultWritePolicy, key, getMapOperation(i) ) @@ -106,7 +104,7 @@ class ZrandmemberCommandListener( } private fun getSetSize(key: Key): Int { - return aeroCtx.client.operate( + return client.operate( defaultWritePolicy, key, MapOperation.size(aeroCtx.bin) )?.getInt(aeroCtx.bin) ?: 0 @@ -115,14 +113,14 @@ class ZrandmemberCommandListener( override fun onSuccess(key: Key?, record: Record?) { try { if (record == null) { - writeNullString(ctx) + writeNullString() } else { synchronized(zrandmemberCommand) { writeResponse(record.bins[aeroCtx.bin]) } } if (zrandmemberCommand.decrementAndGet() == 0) { - ctx.flush() + flushCtxTransactionAware() } } catch (e: Exception) { closeCtx(e) diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/ZrangeCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/ZrangeCommandListener.kt index 769d40b..0dc3a34 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/ZrangeCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/ZrangeCommandListener.kt @@ -8,15 +8,13 @@ import com.aerospike.client.cdt.MapOperation import com.aerospike.client.cdt.MapReturnType import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.util.Intervals import io.netty.channel.ChannelHandlerContext open class ZrangeCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { protected data class LimitArgument( val offset: Int, @@ -88,7 +86,7 @@ open class ZrangeCommandListener( rangeCommand = RangeCommand(cmd, 4) validateAndSet() - aeroCtx.client.operate( + client.operate( null, this, defaultWritePolicy, key, getMapOperation() ) @@ -137,11 +135,11 @@ open class ZrangeCommandListener( override fun onSuccess(key: Key?, record: Record?) { try { if (record == null) { - writeEmptyList(ctx) + writeEmptyList() } else { writeResponse(record.bins[aeroCtx.bin]) } - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } @@ -179,7 +177,7 @@ open class ZrangeCommandListener( l.subList(it.offset, it.offset + it.count) } ?: l }.also { - writeArrayHeader(ctx, it.size * rangeCommand.factor) + writeArrayHeader(it.size * rangeCommand.factor) } } @@ -191,9 +189,8 @@ open class ZrangeCommandListener( } class ZrevrangeCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : ZrangeCommandListener(aeroCtx, ctx) { +) : ZrangeCommandListener(ctx) { override fun validateAndSet() { validateCommon() @@ -204,9 +201,8 @@ class ZrevrangeCommandListener( } open class ZrangebyscoreCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : ZrangeCommandListener(aeroCtx, ctx) { +) : ZrangeCommandListener(ctx) { override fun validateAndSet() { validateCommon() @@ -216,9 +212,8 @@ open class ZrangebyscoreCommandListener( } class ZrevrangebyscoreCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : ZrangebyscoreCommandListener(aeroCtx, ctx) { +) : ZrangebyscoreCommandListener(ctx) { override fun validateAndSet() { super.validateAndSet() @@ -228,9 +223,8 @@ class ZrevrangebyscoreCommandListener( } open class ZrangebylexCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : ZrangeCommandListener(aeroCtx, ctx) { +) : ZrangeCommandListener(ctx) { override fun validateAndSet() { validateCommon() @@ -241,9 +235,8 @@ open class ZrangebylexCommandListener( } class ZrevrangebylexCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : ZrangebylexCommandListener(aeroCtx, ctx) { +) : ZrangebylexCommandListener(ctx) { override fun validateAndSet() { super.validateAndSet() diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/map/ZrangestoreCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/map/ZrangestoreCommandListener.kt index 8b987cf..1e84125 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/map/ZrangestoreCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/map/ZrangestoreCommandListener.kt @@ -7,14 +7,12 @@ import com.aerospike.client.cdt.MapOperation import com.aerospike.client.cdt.MapPolicy import com.aerospike.client.cdt.MapReturnType import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.util.Typed import io.netty.channel.ChannelHandlerContext class ZrangestoreCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : ZrangeCommandListener(aeroCtx, ctx) { +) : ZrangeCommandListener(ctx) { override fun handle(cmd: RequestCommand) { require(cmd.argCount >= 4) { argValidationErrorMsg(cmd) } @@ -24,7 +22,7 @@ class ZrangestoreCommandListener( rangeCommand = RangeCommand(cmd, 5) validateAndSet() - val record = aeroCtx.client.operate( + val record = client.operate( defaultWritePolicy, sourceKey, getMapOperation() ) @@ -37,7 +35,7 @@ class ZrangestoreCommandListener( Value.get(it.value) }.toMap() ) - aeroCtx.client.operate( + client.operate( null, this, defaultWritePolicy, destKey, putOperation ) } @@ -49,11 +47,11 @@ class ZrangestoreCommandListener( override fun onSuccess(key: Key?, record: Record?) { try { if (record == null) { - writeLong(ctx, 0L) + writeLong(0L) } else { - writeLong(ctx, record.getLong(aeroCtx.bin)) + writeLong(record.getLong(aeroCtx.bin)) } - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/scan/HscanCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/scan/HscanCommandListener.kt index 2b52caf..cfdaed4 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/scan/HscanCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/scan/HscanCommandListener.kt @@ -7,14 +7,12 @@ import com.aerospike.client.cdt.MapOperation import com.aerospike.client.cdt.MapReturnType import com.aerospike.client.listener.RecordListener import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import io.netty.channel.ChannelHandlerContext open class HscanCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx), RecordListener { +) : BaseListener(ctx), RecordListener { @Volatile protected lateinit var scanCommand: ScanCommand @@ -24,7 +22,7 @@ open class HscanCommandListener( val key = createKey(cmd.key) scanCommand = ScanCommand(cmd, 3) - aeroCtx.client.operate( + client.operate( null, this, null, key, getOperation() ) @@ -42,23 +40,23 @@ open class HscanCommandListener( override fun onSuccess(key: Key?, record: Record?) { try { if (record == null) { - writeArrayHeader(ctx, 2) - writeSimpleString(ctx, ScanCommand.zeroCursor) - writeEmptyList(ctx) + writeArrayHeader(2) + writeSimpleString(ScanCommand.zeroCursor) + writeEmptyList() } else { val asList = record.bins[aeroCtx.bin] as List<*> - writeArrayHeader(ctx, 2) - writeSimpleString(ctx, getNextCursor(asList.size)) + writeArrayHeader(2) + writeSimpleString(getNextCursor(asList.size)) writeElementsArray(asList) } - ctx.flush() + flushCtxTransactionAware() } catch (e: Exception) { closeCtx(e) } } protected open fun writeElementsArray(list: List<*>) { - writeObjectListStr(ctx, list + writeObjectListStr(list .map { it as Map.Entry<*, *> } .map { it.toPair().toList() }.flatten() ) diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/scan/ScanCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/scan/ScanCommandListener.kt index 46835a2..423e5c8 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/scan/ScanCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/scan/ScanCommandListener.kt @@ -11,15 +11,13 @@ import com.aerospike.client.query.KeyRecord import com.aerospike.client.query.PartitionFilter import com.aerospike.client.query.RegexFlag import com.aerospike.skyhook.command.RequestCommand -import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.listener.BaseListener import com.aerospike.skyhook.listener.scan.ScanCommand.Companion.zeroCursor import io.netty.channel.ChannelHandlerContext class ScanCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : BaseListener(aeroCtx, ctx) { +) : BaseListener(ctx) { private lateinit var scanCommand: ScanCommand private var currentPartition = 0 @@ -38,10 +36,10 @@ class ScanCommandListener( } private fun writeScanResponse() { - writeArrayHeader(ctx, 2) - writeSimpleString(ctx, getNextCursor()) - writeObjectListStr(ctx, recordSet.map { it.key.userKey.`object` as String }) - ctx.flush() + writeArrayHeader(2) + writeSimpleString(getNextCursor()) + writeObjectListStr(recordSet.map { it.key.userKey.`object` as String }) + flushCtxTransactionAware() } private fun getNextCursor(): String { @@ -56,7 +54,7 @@ class ScanCommandListener( val scanPolicy = buildScanPolicy() var filter = getPartitionFilter() while (isScanRequired()) { - aeroCtx.client.scanPartitions( + client.scanPartitions( scanPolicy, filter, aeroCtx.namespace, aeroCtx.set, callback, aeroCtx.bin ) diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/scan/SscanCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/scan/SscanCommandListener.kt index c051c6e..a6645f1 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/scan/SscanCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/scan/SscanCommandListener.kt @@ -3,13 +3,11 @@ package com.aerospike.skyhook.listener.scan import com.aerospike.client.Operation import com.aerospike.client.cdt.MapOperation import com.aerospike.client.cdt.MapReturnType -import com.aerospike.skyhook.config.AerospikeContext import io.netty.channel.ChannelHandlerContext open class SscanCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : HscanCommandListener(aeroCtx, ctx) { +) : HscanCommandListener(ctx) { override fun getOperation(): Operation { return MapOperation.getByIndexRange( @@ -21,6 +19,6 @@ open class SscanCommandListener( } override fun writeElementsArray(list: List<*>) { - writeObject(ctx, list) + writeObject(list) } } diff --git a/src/main/kotlin/com/aerospike/skyhook/listener/scan/ZscanCommandListener.kt b/src/main/kotlin/com/aerospike/skyhook/listener/scan/ZscanCommandListener.kt index af027a0..a94ce5a 100644 --- a/src/main/kotlin/com/aerospike/skyhook/listener/scan/ZscanCommandListener.kt +++ b/src/main/kotlin/com/aerospike/skyhook/listener/scan/ZscanCommandListener.kt @@ -3,13 +3,11 @@ package com.aerospike.skyhook.listener.scan import com.aerospike.client.Operation import com.aerospike.client.cdt.MapOperation import com.aerospike.client.cdt.MapReturnType -import com.aerospike.skyhook.config.AerospikeContext import io.netty.channel.ChannelHandlerContext open class ZscanCommandListener( - aeroCtx: AerospikeContext, ctx: ChannelHandlerContext -) : SscanCommandListener(aeroCtx, ctx) { +) : SscanCommandListener(ctx) { override fun getOperation(): Operation { return MapOperation.getByRankRange( diff --git a/src/main/kotlin/com/aerospike/skyhook/pipeline/AerospikeChannelInitializer.kt b/src/main/kotlin/com/aerospike/skyhook/pipeline/AerospikeChannelInitializer.kt index 0b04f28..a5ed885 100644 --- a/src/main/kotlin/com/aerospike/skyhook/pipeline/AerospikeChannelInitializer.kt +++ b/src/main/kotlin/com/aerospike/skyhook/pipeline/AerospikeChannelInitializer.kt @@ -1,12 +1,18 @@ package com.aerospike.skyhook.pipeline +import com.aerospike.skyhook.config.AerospikeContext +import com.aerospike.skyhook.config.ServerConfiguration import com.aerospike.skyhook.handler.AerospikeChannelHandler +import com.aerospike.skyhook.util.TransactionState +import com.aerospike.skyhook.util.client.AerospikeClientPool import io.netty.channel.ChannelInitializer import io.netty.channel.socket.SocketChannel import io.netty.handler.codec.redis.RedisArrayAggregator import io.netty.handler.codec.redis.RedisBulkStringAggregator import io.netty.handler.codec.redis.RedisDecoder import io.netty.handler.codec.redis.RedisEncoder +import io.netty.util.AttributeKey +import java.util.concurrent.ExecutorService import javax.inject.Inject import javax.inject.Singleton @@ -15,13 +21,35 @@ import javax.inject.Singleton */ @Singleton class AerospikeChannelInitializer @Inject constructor( - private val aerospikeChannelHandler: AerospikeChannelHandler + private val config: ServerConfiguration, + private val clientPool: AerospikeClientPool, + private val aerospikeChannelHandler: AerospikeChannelHandler, + private val executorService: ExecutorService ) : ChannelInitializer() { + companion object { + val authDetailsAttrKey: AttributeKey = AttributeKey.valueOf("authDetails") + val aeroCtxAttrKey: AttributeKey = AttributeKey.valueOf("aeroCtx") + val clientPoolAttrKey: AttributeKey = AttributeKey.valueOf("clientPool") + val transactionAttrKey: AttributeKey = AttributeKey.valueOf("transactionState") + } + override fun initChannel(ch: SocketChannel) { ch.pipeline().addLast( RedisDecoder(true), RedisBulkStringAggregator(), RedisArrayAggregator(), RedisEncoder(), aerospikeChannelHandler ) + + ch.attr(aeroCtxAttrKey).set( + AerospikeContext( + config.namespase, + config.set, + config.bin, + config.typeBin + ) + ) + + ch.attr(clientPoolAttrKey).set(clientPool) + ch.attr(transactionAttrKey).set(TransactionState(executorService)) } } diff --git a/src/main/kotlin/com/aerospike/skyhook/util/Extensions.kt b/src/main/kotlin/com/aerospike/skyhook/util/Extensions.kt index bfb7ee9..57e00a5 100644 --- a/src/main/kotlin/com/aerospike/skyhook/util/Extensions.kt +++ b/src/main/kotlin/com/aerospike/skyhook/util/Extensions.kt @@ -1,7 +1,18 @@ package com.aerospike.skyhook.util +import io.netty.channel.ChannelHandlerContext + fun List.toPair(): Pair { require(this.size == 2) { "List is not of length 2" } val (a, b) = this return Pair(a, b) } + +@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") +fun ChannelHandlerContext.wait() = (this as Object).wait() + +@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") +fun ChannelHandlerContext.wait(timeout: Long) = (this as Object).wait(timeout) + +@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") +fun ChannelHandlerContext.notify() = (this as Object).notify() diff --git a/src/main/kotlin/com/aerospike/skyhook/util/RedisCommandsDetails.kt b/src/main/kotlin/com/aerospike/skyhook/util/RedisCommandsDetails.kt deleted file mode 100644 index 223e19a..0000000 --- a/src/main/kotlin/com/aerospike/skyhook/util/RedisCommandsDetails.kt +++ /dev/null @@ -1,110 +0,0 @@ -package com.aerospike.skyhook.util - -import com.aerospike.skyhook.command.RedisCommandDetails - -object RedisCommandsDetails { - - val getCommand = RedisCommandDetails("get", 2, arrayListOf("readonly", "fast"), 1, 1, 1) - val mgetCommand = RedisCommandDetails("mget", -2, arrayListOf("readonly", "fast"), 1, -1, 1) - val getsetCommand = RedisCommandDetails("getset", 3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val setCommand = RedisCommandDetails("set", -3, arrayListOf("write", "denyoom"), 1, 1, 1) - val setexCommand = RedisCommandDetails("setex", 4, arrayListOf("write", "denyoom"), 1, 1, 1) - val psetexCommand = RedisCommandDetails("psetex", 4, arrayListOf("write", "denyoom"), 1, 1, 1) - val setnxCommand = RedisCommandDetails("setnx", 3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val msetCommand = RedisCommandDetails("mset", -3, arrayListOf("write", "denyoom"), 1, -1, 2) - val msetnxCommand = RedisCommandDetails("msetnx", -3, arrayListOf("write", "denyoom"), 1, -1, 2) - val existsCommand = RedisCommandDetails("exists", -2, arrayListOf("readonly", "fast"), 1, -1, 1) - val expireCommand = RedisCommandDetails("expire", 3, arrayListOf("write", "fast"), 1, 1, 1) - val pexpireCommand = RedisCommandDetails("pexpire", 3, arrayListOf("write", "fast"), 1, 1, 1) - val expireatCommand = RedisCommandDetails("expireat", 3, arrayListOf("write", "fast"), 1, 1, 1) - val pexpireatCommand = RedisCommandDetails("pexpireat", 3, arrayListOf("write", "fast"), 1, 1, 1) - val persistCommand = RedisCommandDetails("persist", 2, arrayListOf("write", "fast"), 1, 1, 1) - val appendCommand = RedisCommandDetails("append", 3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val incrCommand = RedisCommandDetails("incr", 2, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val incrbyCommand = RedisCommandDetails("incrby", 3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val incrbyfloatCommand = RedisCommandDetails("incrbyfloat", 3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val decrCommand = RedisCommandDetails("decr", 2, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val decrbyCommand = RedisCommandDetails("decrby", 3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val strlenCommand = RedisCommandDetails("strlen", 2, arrayListOf("readonly", "fast"), 1, 1, 1) - val ttlCommand = RedisCommandDetails("ttl", 2, arrayListOf("readonly", "random", "fast"), 1, 1, 1) - val pttlCommand = RedisCommandDetails("pttl", 2, arrayListOf("readonly", "random", "fast"), 1, 1, 1) - val delCommand = RedisCommandDetails("del", -2, arrayListOf("write"), 1, -1, 1) - val unlinkCommand = RedisCommandDetails("unlink", -2, arrayListOf("write", "fast"), 1, -1, 1) - val randomkeyCommand = RedisCommandDetails("randomkey", 1, arrayListOf("readonly", "random"), 0, 0, 0) - val touchCommand = RedisCommandDetails("touch", -2, arrayListOf("readonly", "fast"), 1, -1, 1) - val typeCommand = RedisCommandDetails("type", 2, arrayListOf("readonly", "fast"), 1, 1, 1) - - val lpushCommand = RedisCommandDetails("lpush", -3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val lpushxCommand = RedisCommandDetails("lpushx", -3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val rpushCommand = RedisCommandDetails("rpush", -3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val rpushxCommand = RedisCommandDetails("rpushx", -3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val lindexCommand = RedisCommandDetails("lindex", 3, arrayListOf("readonly"), 1, 1, 1) - val llenCommand = RedisCommandDetails("llen", 2, arrayListOf("readonly", "fast"), 1, 1, 1) - val lpopCommand = RedisCommandDetails("lpop", -2, arrayListOf("write", "fast"), 1, 1, 1) - val rpopCommand = RedisCommandDetails("rpop", -2, arrayListOf("write", "fast"), 1, 1, 1) - val lrangeCommand = RedisCommandDetails("lrange", 4, arrayListOf("readonly"), 1, 1, 1) - - val hsetCommand = RedisCommandDetails("hset", -4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val hsetnxCommand = RedisCommandDetails("hsetnx", 4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val hmsetCommand = RedisCommandDetails("hmset", -4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val saddCommand = RedisCommandDetails("sadd", -3, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val hexistsCommand = RedisCommandDetails("hexists", 3, arrayListOf("readonly", "fast"), 1, 1, 1) - val sismemberCommand = RedisCommandDetails("sismember", 3, arrayListOf("readonly", "fast"), 1, 1, 1) - val hgetCommand = RedisCommandDetails("hget", 3, arrayListOf("readonly", "fast"), 1, 1, 1) - val hmgetCommand = RedisCommandDetails("hmget", -3, arrayListOf("readonly", "fast"), 1, 1, 1) - val hgetallCommand = RedisCommandDetails("hgetall", 2, arrayListOf("readonly", "random"), 1, 1, 1) - val hvalsCommand = RedisCommandDetails("hvals", 2, arrayListOf("readonly", "sort_for_script"), 1, 1, 1) - val hkeysCommand = RedisCommandDetails("hkeys", 2, arrayListOf("readonly", "sort_for_script"), 1, 1, 1) - val smembersCommand = RedisCommandDetails("smembers", 2, arrayListOf("readonly", "sort_for_script"), 1, 1, 1) - val hincrbyCommand = RedisCommandDetails("hincrby", 4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val hincrbyfloatCommand = RedisCommandDetails("hincrbyfloat", 4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val hstrlenCommand = RedisCommandDetails("hstrlen", 3, arrayListOf("readonly", "fast"), 1, 1, 1) - val hlenCommand = RedisCommandDetails("hlen", 2, arrayListOf("readonly", "fast"), 1, 1, 1) - val scardCommand = RedisCommandDetails("scard", 2, arrayListOf("readonly", "fast"), 1, 1, 1) - val zcardCommand = RedisCommandDetails("zcard", 2, arrayListOf("readonly", "fast"), 1, 1, 1) - val hdelCommand = RedisCommandDetails("hdel", -3, arrayListOf("write", "fast"), 1, 1, 1) - val sremCommand = RedisCommandDetails("srem", -3, arrayListOf("write", "fast"), 1, 1, 1) - val zremCommand = RedisCommandDetails("zrem", -3, arrayListOf("write", "fast"), 1, 1, 1) - val sunionCommand = RedisCommandDetails("sunion", -2, arrayListOf("readonly", "sort_for_script"), 1, -1, 1) - val sinterCommand = RedisCommandDetails("sinter", -2, arrayListOf("readonly", "sort_for_script"), 1, -1, 1) - val sunionstoreCommand = RedisCommandDetails("sunionstore", -3, arrayListOf("write", "denyoom"), 1, -1, 1) - val sinterstoreCommand = RedisCommandDetails("sinterstore", -3, arrayListOf("write", "denyoom"), 1, -1, 1) - val zmscoreCommand = RedisCommandDetails("zmscore", -3, arrayListOf("readonly", "fast"), 1, 1, 1) - val zrankCommand = RedisCommandDetails("zrank", 3, arrayListOf("readonly", "fast"), 1, 1, 1) - val zincrbyCommand = RedisCommandDetails("zincrby", 4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val zaddCommand = RedisCommandDetails("zadd", -4, arrayListOf("write", "denyoom", "fast"), 1, 1, 1) - val zpopmaxCommand = RedisCommandDetails("zpopmax", -2, arrayListOf("write", "fast"), 1, 1, 1) - val zpopminCommand = RedisCommandDetails("zpopmin", -2, arrayListOf("write", "fast"), 1, 1, 1) - val zrandmemberCommand = RedisCommandDetails("zrandmember", -2, arrayListOf("readonly", "random"), 1, 1, 1) - val zcountCommand = RedisCommandDetails("zcount", 4, arrayListOf("readonly", "fast"), 1, 1, 1) - val zlexcountCommand = RedisCommandDetails("zlexcount", 4, arrayListOf("readonly", "fast"), 1, 1, 1) - val zremrangebyscoreCommand = RedisCommandDetails("zremrangebyscore", 4, arrayListOf("write"), 1, 1, 1) - val zremrangebyrankCommand = RedisCommandDetails("zremrangebyrank", 4, arrayListOf("write"), 1, 1, 1) - val zremrangebylexCommand = RedisCommandDetails("zremrangebylex", 4, arrayListOf("write"), 1, 1, 1) - val zrangeCommand = RedisCommandDetails("zrange", -4, arrayListOf("readonly"), 1, 1, 1) - val zrangestoreCommand = RedisCommandDetails("zrangestore", -5, arrayListOf("write", "denyoom"), 1, 2, 1) - val zrevrangeCommand = RedisCommandDetails("zrevrange", -4, arrayListOf("readonly"), 1, 1, 1) - val zrangebyscoreCommand = RedisCommandDetails("zrangebyscore", -4, arrayListOf("readonly"), 1, 1, 1) - val zrevrangebyscoreCommand = RedisCommandDetails("zrevrangebyscore", -4, arrayListOf("readonly"), 1, 1, 1) - val zrangebylexCommand = RedisCommandDetails("zrangebylex", -4, arrayListOf("readonly"), 1, 1, 1) - val zrevrangebylexCommand = RedisCommandDetails("zrevrangebylex", -4, arrayListOf("readonly"), 1, 1, 1) - - val scanCommand = RedisCommandDetails("scan", -2, arrayListOf("readonly", "random"), 0, 0, 0) - val hscanCommand = RedisCommandDetails("hscan", -3, arrayListOf("readonly", "random"), 1, 1, 1) - val sscanCommand = RedisCommandDetails("sscan", -3, arrayListOf("readonly", "random"), 1, 1, 1) - val zscanCommand = RedisCommandDetails("zscan", -3, arrayListOf("readonly", "random"), 1, 1, 1) - - val flushdbCommand = RedisCommandDetails("flushdb", -1, arrayListOf("write"), 0, 0, 0) - val flushallCommand = RedisCommandDetails("flushall", -1, arrayListOf("write"), 0, 0, 0) - val dbsizeCommand = RedisCommandDetails("dbsize", 1, arrayListOf("readonly", "fast"), 0, 0, 0) - - val pingCommand = RedisCommandDetails("ping", -1, arrayListOf("stale", "fast"), 0, 0, 0) - val echoCommand = RedisCommandDetails("echo", 2, arrayListOf("fast"), 0, 0, 0) - val lolwutCommand = RedisCommandDetails("lolwut", -1, arrayListOf("readonly", "fast"), 0, 0, 0) - val timeCommand = RedisCommandDetails("time", 1, arrayListOf("random", "loading", "stale", "fast"), 0, 0, 0) - val resetCommand = RedisCommandDetails("reset", 1, arrayListOf("noscript", "loading", "stale", "fast"), 0, 0, 0) - val saveCommand = RedisCommandDetails("save", 1, arrayListOf("admin", "noscript"), 0, 0, 0) - val bgsaveCommand = RedisCommandDetails("bgsave", -1, arrayListOf("admin", "noscript"), 0, 0, 0) - val commandCommand = RedisCommandDetails("command", -1, arrayListOf("random", "loading", "stale"), 0, 0, 0) - -} diff --git a/src/main/kotlin/com/aerospike/skyhook/util/TransactionState.kt b/src/main/kotlin/com/aerospike/skyhook/util/TransactionState.kt new file mode 100644 index 0000000..a560ae5 --- /dev/null +++ b/src/main/kotlin/com/aerospike/skyhook/util/TransactionState.kt @@ -0,0 +1,21 @@ +package com.aerospike.skyhook.util + +import com.aerospike.skyhook.command.RequestCommand +import java.util.* +import java.util.concurrent.ExecutorService + +class TransactionState(val pool: ExecutorService) { + var inTransaction: Boolean = false + private set + + val commands: LinkedList = LinkedList() + + fun startTransaction() { + inTransaction = true + } + + fun clear() { + inTransaction = false + commands.clear() + } +} diff --git a/src/main/kotlin/com/aerospike/skyhook/util/client/AerospikeClientPool.kt b/src/main/kotlin/com/aerospike/skyhook/util/client/AerospikeClientPool.kt new file mode 100644 index 0000000..655ff30 --- /dev/null +++ b/src/main/kotlin/com/aerospike/skyhook/util/client/AerospikeClientPool.kt @@ -0,0 +1,10 @@ +package com.aerospike.skyhook.util.client + +import com.aerospike.client.IAerospikeClient + +interface AerospikeClientPool { + + fun getClient(authDetails: AuthDetails): IAerospikeClient? + + fun getClient(authDetailsHash: String?): IAerospikeClient +} diff --git a/src/main/kotlin/com/aerospike/skyhook/util/client/AerospikeClientPoolImpl.kt b/src/main/kotlin/com/aerospike/skyhook/util/client/AerospikeClientPoolImpl.kt new file mode 100644 index 0000000..36c2760 --- /dev/null +++ b/src/main/kotlin/com/aerospike/skyhook/util/client/AerospikeClientPoolImpl.kt @@ -0,0 +1,63 @@ +package com.aerospike.skyhook.util.client + +import com.aerospike.client.AerospikeClient +import com.aerospike.client.Host +import com.aerospike.client.IAerospikeClient +import com.aerospike.client.policy.ClientPolicy +import com.aerospike.skyhook.config.ServerConfiguration +import com.google.common.cache.Cache +import com.google.common.cache.CacheBuilder +import mu.KotlinLogging +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AerospikeClientPoolImpl @Inject constructor( + private val config: ServerConfiguration, + private val clientPolicy: ClientPolicy +) : AerospikeClientPool { + + companion object { + private val log = KotlinLogging.logger {} + + const val defaultAerospikePort = 3000 + private const val clientPoolSize = 8L + } + + private val clientPool: Cache = + CacheBuilder.newBuilder().maximumSize(clientPoolSize).build() + + private val defaultClient: IAerospikeClient by lazy { + createClient(clientPolicy) + } + + override fun getClient(authDetails: AuthDetails): IAerospikeClient? { + val key = authDetails.hashString + return Optional.ofNullable(clientPool.getIfPresent(key)).orElseGet { + val policy = ClientPolicy(clientPolicy) + policy.user = authDetails.user + policy.password = authDetails.password + + try { + val client = createClient(policy) + log.info("Cache a new AerospikeClient") + clientPool.put(key, client) + client + } catch (e: Exception) { + null + } + } + } + + override fun getClient(authDetailsHash: String?): IAerospikeClient { + return authDetailsHash?.let { clientPool.getIfPresent(it) } ?: defaultClient + } + + private fun createClient(policy: ClientPolicy): IAerospikeClient { + return AerospikeClient( + policy, + *Host.parseHosts(config.hostList, defaultAerospikePort) + ) + } +} diff --git a/src/main/kotlin/com/aerospike/skyhook/util/client/AuthDetails.kt b/src/main/kotlin/com/aerospike/skyhook/util/client/AuthDetails.kt new file mode 100644 index 0000000..328f139 --- /dev/null +++ b/src/main/kotlin/com/aerospike/skyhook/util/client/AuthDetails.kt @@ -0,0 +1,15 @@ +package com.aerospike.skyhook.util.client + +import com.google.common.hash.Hashing + +data class AuthDetails( + val user: String, + val password: String +) { + @Suppress("UnstableApiUsage") + val hashString: String by lazy { + Hashing.sha256() + .hashBytes(toString().encodeToByteArray()) + .toString() + } +} diff --git a/src/test/kotlin/com/aerospike/skyhook/SkyhookIntegrationTestBase.kt b/src/test/kotlin/com/aerospike/skyhook/SkyhookIntegrationTestBase.kt index 6d69764..fc77347 100644 --- a/src/test/kotlin/com/aerospike/skyhook/SkyhookIntegrationTestBase.kt +++ b/src/test/kotlin/com/aerospike/skyhook/SkyhookIntegrationTestBase.kt @@ -1,18 +1,24 @@ package com.aerospike.skyhook import com.aerospike.client.Bin -import com.aerospike.client.IAerospikeClient import com.aerospike.client.Key import com.aerospike.client.Value import com.aerospike.skyhook.command.RedisCommand +import com.aerospike.skyhook.config.AerospikeContext import com.aerospike.skyhook.config.ServerConfiguration import com.aerospike.skyhook.handler.AerospikeChannelHandler +import com.aerospike.skyhook.pipeline.AerospikeChannelInitializer.Companion.aeroCtxAttrKey +import com.aerospike.skyhook.pipeline.AerospikeChannelInitializer.Companion.clientPoolAttrKey +import com.aerospike.skyhook.pipeline.AerospikeChannelInitializer.Companion.transactionAttrKey import com.aerospike.skyhook.util.ScanResponse +import com.aerospike.skyhook.util.TransactionState +import com.aerospike.skyhook.util.client.AerospikeClientPool import com.google.inject.Guice import io.netty.buffer.Unpooled.buffer import io.netty.channel.embedded.EmbeddedChannel import io.netty.handler.codec.redis.* import org.junit.jupiter.api.AfterEach +import java.util.concurrent.ExecutorService import kotlin.experimental.and import kotlin.test.assertEquals @@ -22,9 +28,10 @@ abstract class SkyhookIntegrationTestBase { private val config = ServerConfiguration() private val injector = Guice.createInjector(SkyhookModule(config)) - protected val client: IAerospikeClient = injector.getInstance(IAerospikeClient::class.java) + protected val clientPool: AerospikeClientPool = injector.getInstance(AerospikeClientPool::class.java) private val aerospikeChannelHandler = injector.getInstance(AerospikeChannelHandler::class.java) + protected val executorService: ExecutorService = injector.getInstance(ExecutorService::class.java) @JvmStatic protected val ok = "OK" @@ -49,6 +56,20 @@ abstract class SkyhookIntegrationTestBase { RedisEncoder(), ) + init { + channel.attr(aeroCtxAttrKey).set( + AerospikeContext( + config.namespase, + config.set, + config.bin, + config.typeBin + ) + ) + + channel.attr(clientPoolAttrKey).set(clientPool) + channel.attr(transactionAttrKey).set(TransactionState(executorService)) + } + protected fun aeroKey(key: Any): Key { return Key(config.namespase, config.set, Value.get(key)) } @@ -74,6 +95,11 @@ abstract class SkyhookIntegrationTestBase { channel.writeInbound(byteBuf) } + protected fun readArrayLen(): Long { + Thread.sleep(sleepMillis) + return channel.readOutbound().length() + } + protected fun readStringArray(): Array { Thread.sleep(sleepMillis) val len = channel.readOutbound().length() diff --git a/src/test/kotlin/com/aerospike/skyhook/TransactionTest.kt b/src/test/kotlin/com/aerospike/skyhook/TransactionTest.kt new file mode 100644 index 0000000..b5c573d --- /dev/null +++ b/src/test/kotlin/com/aerospike/skyhook/TransactionTest.kt @@ -0,0 +1,60 @@ +package com.aerospike.skyhook + +import com.aerospike.skyhook.command.RedisCommand +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class TransactionTest() : SkyhookIntegrationTestBase() { + + @Test + fun testTransaction() { + writeCommand(RedisCommand.MULTI.name) + assertEquals(ok, readString()) + + writeCommand("${RedisCommand.SET.name} key1 val1") + assertEquals("QUEUED", readString()) + + writeCommand("${RedisCommand.GET.name} key1") + assertEquals("QUEUED", readString()) + + writeCommand("NE abc") + assert(readError().isNotEmpty()) + + writeCommand(RedisCommand.PING.name) + assertEquals("QUEUED", readString()) + + writeCommand(RedisCommand.EXEC.name) + assertEquals(3L, readArrayLen()) + + assertEquals(ok, readString()) + assertEquals("val1", readFullBulkString()) + assertEquals("PONG", readString()) + } + + @Test + fun testDiscardTransaction() { + writeCommand(RedisCommand.MULTI.name) + assertEquals(ok, readString()) + + writeCommand("${RedisCommand.SET.name} key1 val1") + assertEquals("QUEUED", readString()) + + writeCommand("${RedisCommand.GET.name} key1") + assertEquals("QUEUED", readString()) + + writeCommand(RedisCommand.DISCARD.name) + assertEquals(ok, readString()) + + writeCommand(RedisCommand.EXEC.name) + assert(readError().isNotEmpty()) + + writeCommand(RedisCommand.PING.name) + assertEquals("PONG", readString()) + } + + @Test + fun testExecWithoutMultiTransaction() { + writeCommand(RedisCommand.EXEC.name) + assert(readError().isNotEmpty()) + } +}