Skip to content

Commit bd5990a

Browse files
committed
Add client with very reduced command set
1 parent 9c61fa7 commit bd5990a

File tree

20 files changed

+1127
-1
lines changed

20 files changed

+1127
-1
lines changed

.idea/codeStyles/Project.xml

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build.gradle.kts

+21
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
val bigNumVersion: String by project
2+
val kotlinCoroutinesVersion: String by project
3+
val kotlinLoggingVersion: String by project
4+
val ktorVersion: String by project
5+
16
plugins {
27
kotlin("multiplatform")
38
}
@@ -6,6 +11,8 @@ group = "com.github.domgew"
611
version = "0.0.1"
712

813
kotlin {
14+
explicitApi()
15+
916
jvm {
1017
jvmToolchain(17)
1118
}
@@ -24,11 +31,25 @@ kotlin {
2431
val commonMain by getting {
2532
dependencies {
2633
implementation(kotlin("stdlib"))
34+
35+
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutinesVersion")
36+
implementation("io.ktor:ktor-network:$ktorVersion")
37+
38+
api("com.ionspin.kotlin:bignum:$bigNumVersion")
39+
api("io.github.oshai:kotlin-logging:$kotlinLoggingVersion")
2740
}
2841
}
2942
val commonTest by getting {
3043
dependencies {
3144
implementation(kotlin("test"))
45+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutinesVersion")
46+
}
47+
48+
tasks.withType<Test> {
49+
environment(
50+
"REDIS_PORT",
51+
System.getProperty("REDIS_PORT") ?: "6379",
52+
)
3253
}
3354
}
3455
}

gradle.properties

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
kotlin.code.style=official
2+
3+
bigNumVersion=0.3.8
4+
foojayVersion=0.5.0
5+
kotlinCoroutinesVersion=1.7.3
6+
kotlinLoggingVersion=6.0.1
27
kotlinVersion=1.9.22
38
ktorVersion=2.3.6
4-
foojayVersion=0.5.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.github.domgew.kredis
2+
3+
import com.github.domgew.kredis.arguments.SyncOptions
4+
import com.github.domgew.kredis.impl.DefaultKredisClient
5+
6+
/**
7+
* Each connection should have its own instance.
8+
*
9+
* Should be used with `client.use { ... }`.
10+
*/
11+
@OptIn(ExperimentalStdlibApi::class)
12+
public interface KredisClient: AutoCloseable {
13+
public companion object {
14+
public fun newClient(configuration: KredisConfiguration): KredisClient {
15+
return DefaultKredisClient(
16+
configuration = configuration,
17+
)
18+
}
19+
}
20+
21+
public val isConnected: Boolean
22+
public suspend fun connect()
23+
public suspend fun closeAsync()
24+
25+
/**
26+
* Sends a message to the server which should be returned unchanged.
27+
* @return The response - should be the [content]
28+
*/
29+
public suspend fun ping(
30+
content: String = "PING",
31+
): String?
32+
33+
/**
34+
* @return The redis server version
35+
*/
36+
public suspend fun serverVersion(): String
37+
38+
/**
39+
* Clears all redis DBs
40+
* @return Whether the server responded with "OK"
41+
*/
42+
public suspend fun flushAll(sync: SyncOptions): Boolean
43+
44+
/**
45+
* Gets the value behind the given [key].
46+
* @return The value or NULL
47+
*/
48+
public suspend fun get(
49+
key: String,
50+
): String?
51+
52+
// TODO: add options and improve result
53+
public suspend fun set(
54+
key: String,
55+
value: String,
56+
): String?
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.github.domgew.kredis
2+
3+
public data class KredisConfiguration(
4+
/**
5+
* Host name or IP, where the redis server is reachable
6+
*/
7+
val host: String,
8+
/**
9+
* Host port, where the redis server is reachable
10+
*/
11+
val port: Int = 6379,
12+
/**
13+
* The maximum time it may take to establish a connection with the redis server
14+
*/
15+
val connectionTimeoutMillis: Long,
16+
/**
17+
* Whether to keep the socket connection alive
18+
*/
19+
val keepAlive: Boolean = false,
20+
/**
21+
* The maximum time it may take the server to respond with the full content - this also includes parsing
22+
*/
23+
val readTimeoutMillis: Long? = null,
24+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.github.domgew.kredis
2+
3+
public sealed interface KredisException {
4+
public data object ConnectionTimeout : Exception("Connection timed out"), KredisException
5+
public class WrongResponse(
6+
message: String,
7+
) : Exception("Wrong response: $message"), KredisException
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.github.domgew.kredis.arguments
2+
3+
internal interface RedisCommandArgument
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.github.domgew.kredis.arguments
2+
3+
public enum class SyncOptions {
4+
SYNC,
5+
ASYNC,
6+
;
7+
8+
override fun toString(): String =
9+
this.name
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.github.domgew.kredis.commands
2+
3+
import com.github.domgew.kredis.impl.RedisMessage
4+
5+
internal interface KredisCommand {
6+
fun toRedisMessage(): RedisMessage
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.github.domgew.kredis.commands
2+
3+
import com.github.domgew.kredis.impl.RedisMessage
4+
5+
internal data class KredisStringArrayCommand(
6+
val values: List<String>,
7+
): KredisCommand {
8+
override fun toRedisMessage(): RedisMessage =
9+
RedisMessage.ArrayMessage(
10+
value = values.map {
11+
RedisMessage.BulkStringMessage(
12+
value = it,
13+
)
14+
},
15+
)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.github.domgew.kredis.impl
2+
3+
import com.github.domgew.kredis.KredisClient
4+
import com.github.domgew.kredis.KredisConfiguration
5+
import com.github.domgew.kredis.KredisException
6+
import com.github.domgew.kredis.commands.KredisCommand
7+
import io.ktor.network.selector.SelectorManager
8+
import io.ktor.network.sockets.InetSocketAddress
9+
import io.ktor.network.sockets.Socket
10+
import io.ktor.network.sockets.aSocket
11+
import io.ktor.network.sockets.awaitClosed
12+
import io.ktor.network.sockets.openReadChannel
13+
import io.ktor.network.sockets.openWriteChannel
14+
import io.ktor.utils.io.ByteReadChannel
15+
import io.ktor.utils.io.ByteWriteChannel
16+
import kotlinx.coroutines.Dispatchers
17+
import kotlinx.coroutines.IO
18+
import kotlinx.coroutines.TimeoutCancellationException
19+
import kotlinx.coroutines.isActive
20+
import kotlinx.coroutines.sync.Mutex
21+
import kotlinx.coroutines.withTimeout
22+
23+
internal abstract class AbstractKredisClient(
24+
protected val configuration: KredisConfiguration,
25+
): KredisClient {
26+
protected val lock = Mutex()
27+
private var _socket: Socket? = null
28+
private var _writeChannel: ByteWriteChannel? = null
29+
private var _readChannel: ByteReadChannel? = null
30+
31+
final override val isConnected: Boolean
32+
get() = _socket != null
33+
&& _writeChannel != null
34+
&& _readChannel != null
35+
&& _socket?.isActive == true
36+
37+
private suspend fun doConnect() {
38+
val socket = try {
39+
withTimeout(configuration.connectionTimeoutMillis) {
40+
aSocket(SelectorManager(Dispatchers.IO))
41+
.tcp()
42+
.connect(
43+
remoteAddress = InetSocketAddress(
44+
hostname = configuration.host,
45+
port = configuration.port,
46+
),
47+
configure = {
48+
keepAlive = configuration.keepAlive
49+
},
50+
)
51+
}
52+
} catch (ex: TimeoutCancellationException) {
53+
throw KredisException.ConnectionTimeout
54+
}
55+
this._socket = socket
56+
this._readChannel = socket.openReadChannel()
57+
this._writeChannel = socket.openWriteChannel(autoFlush = false)
58+
}
59+
60+
protected suspend fun ensureConnected() {
61+
if (!isConnected) {
62+
doConnect()
63+
}
64+
}
65+
66+
protected suspend fun doClose() {
67+
_socket?.close()
68+
_socket?.awaitClosed()
69+
}
70+
71+
protected suspend fun executeCommand(
72+
command: KredisCommand,
73+
): RedisMessage {
74+
ensureConnected()
75+
command.toRedisMessage().writeTo(_writeChannel!!)
76+
_writeChannel!!.flush()
77+
return RedisMessage.parse(_readChannel!!)
78+
}
79+
}

0 commit comments

Comments
 (0)