Skip to content

Commit

Permalink
Merge pull request #56 from crowdproj/wip-23
Browse files Browse the repository at this point in the history
app-tts: use redis as a cache
  • Loading branch information
sszuev authored Aug 25, 2024
2 parents f17c5ef + f221708 commit cbb8ba2
Show file tree
Hide file tree
Showing 16 changed files with 166 additions and 20 deletions.
4 changes: 4 additions & 0 deletions app-tts/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ dependencies {
val testContainersVersion: String by project
val mockkVersion: String by project
val typesafeConfigVersion: String by project
val lettuceVersion: String by project

implementation(project(":tts-lib"))
implementation(project(":common"))
implementation(project(":core"))
implementation(project(":utilities"))

implementation("io.nats:jnats:$natsVersion")
implementation("io.lettuce:lettuce-core:$lettuceVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutinesVersion")
implementation("com.typesafe:config:$typesafeConfigVersion")

Expand All @@ -46,8 +48,10 @@ tasks.test {

tasks.dockerCreateDockerfile {
arg("TTS_SERVER_NATS_HOST")
arg("TTS_SERVER_REDIS_HOST")
arg("TTS_SERVICE_VOICERSS_KEY")
environmentVariable("TTS_SERVER_NATS_HOST", "\${TTS_SERVER_NATS_HOST}")
environmentVariable("TTS_SERVER_REDIS_HOST", "\${TTS_SERVER_REDIS_HOST}")
environmentVariable("TTS_SERVICE_VOICERSS_KEY", "\${TTS_SERVICE_VOICERSS_KEY}")
}

Expand Down
23 changes: 23 additions & 0 deletions app-tts/src/main/kotlin/GetResourceListeners.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.gitlab.sszuev.flashcards.speaker

import io.lettuce.core.api.sync.RedisCommands
import org.slf4j.LoggerFactory
import java.time.Duration
import java.time.Instant

private val logger = LoggerFactory.getLogger("com.gitlab.sszuev.flashcards.speaker.GetResourceListeners")

fun onGetResource(commands: RedisCommands<String, String>) = try {
val res1 = commands.incr("words.count.total")
var res2 = commands.incr("words.count.daily")
val date = commands.get("words.date")?.let { Instant.parse(it) }
val now = Instant.now()
if (date != null && Duration.between(date, now).seconds > 24 * 60 * 60) {
// new day
res2 = commands.del("words.count.daily")
commands.set("words.date", now.toString())
}
logger.info("Total count $res1, today's count $res2")
} catch (ex: Exception) {
logger.error("Unexpected error on get resource", ex)
}
2 changes: 1 addition & 1 deletion app-tts/src/main/kotlin/NatsConfig.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.gitlab.sszuev.flashcards.speaker

data class NatsConfig(
val url: String = "nats://${TTSServerSettings.host}:${TTSServerSettings.port}",
val url: String = "nats://${TTSServerSettings.natsHost}:${TTSServerSettings.natsPort}",
val user: String = TTSServerSettings.user,
val password: String = TTSServerSettings.password,
val topic: String = TTSServerSettings.topic,
Expand Down
5 changes: 5 additions & 0 deletions app-tts/src/main/kotlin/RedisConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.gitlab.sszuev.flashcards.speaker

data class RedisConfig(
val url: String = "redis://${TTSServerSettings.redisHost}:${TTSServerSettings.redisPort}",
)
36 changes: 36 additions & 0 deletions app-tts/src/main/kotlin/RedisConnectionFactory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.gitlab.sszuev.flashcards.speaker

import io.lettuce.core.RedisClient
import io.lettuce.core.api.sync.RedisCommands
import io.lettuce.core.codec.ByteArrayCodec
import io.lettuce.core.codec.RedisCodec
import io.lettuce.core.codec.StringCodec

class RedisConnectionFactory(
connectionUrl: String = "redis://localhost:6379",
) : AutoCloseable {

private val client by lazy {
RedisClient.create(connectionUrl)
}
private val stringToStringConnection by lazy {
client.connect()
}
private val stringToByteArrayConnection by lazy {
client.connect(RedisCodec.of(StringCodec(), ByteArrayCodec()))
}

val stringToByteArrayCommands: RedisCommands<String, ByteArray> by lazy {
stringToByteArrayConnection.sync()
}

val stringToStringCommands: RedisCommands<String, String> by lazy {
stringToStringConnection.sync()
}

override fun close() {
stringToByteArrayConnection.close()
stringToStringConnection.close()
client.shutdown()
}
}
28 changes: 28 additions & 0 deletions app-tts/src/main/kotlin/RedisResourceCache.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.gitlab.sszuev.flashcards.speaker

import io.lettuce.core.api.sync.RedisCommands
import org.slf4j.LoggerFactory

private val logger = LoggerFactory.getLogger(RedisResourceCache::class.java)

class RedisResourceCache(
private val commands: RedisCommands<String, ByteArray>
) : ResourceCache {

override fun get(id: String): ByteArray? = try {
commands.get(id)
} catch (ex: Exception) {
logger.error("unexpected error while redis#get", ex)
null
}

override fun put(id: String, data: ByteArray) {
try {
if (commands.set(id, data) != "OK") {
logger.error("Can't redis#set")
}
} catch (ex: Exception) {
logger.error("unexpected error while redis#put", ex)
}
}
}
20 changes: 14 additions & 6 deletions app-tts/src/main/kotlin/TTSServerMain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,24 @@ import kotlin.concurrent.thread
private val logger = LoggerFactory.getLogger("com.gitlab.sszuev.flashcards.speaker.TTSServerMain")

fun main() {
val config = NatsConfig()
val natsConfig = NatsConfig()
val redisConfig = RedisConfig()
val redis = RedisConnectionFactory(
connectionUrl = redisConfig.url,
)
val processor = NatsTTSServerProcessorImpl(
service = createTTSService(),
topic = config.topic,
group = config.group,
connectionUrl = config.url,
service = createTTSService(
cache = RedisResourceCache(redis.stringToByteArrayCommands),
onGetResource = { onGetResource(redis.stringToStringCommands) },
),
topic = natsConfig.topic,
group = natsConfig.group,
connectionUrl = natsConfig.url,
)
Runtime.getRuntime().addShutdownHook(thread(start = false) {
logger.info("Close connection on shutdown.")
logger.info("Close connections on shutdown.")
processor.close()
redis.close()
})
logger.info("Start processing.")
TTSServerController(processor).start()
Expand Down
12 changes: 8 additions & 4 deletions app-tts/src/main/kotlin/TTSServerSettings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ object TTSServerSettings {

private val conf: Config = ConfigFactory.load()

val host = conf.get(key = "tts-server.nats.host", default = "localhost")
val port = conf.get(key = "tts-server.nats.port", default = 4222)
val natsHost = conf.get(key = "tts-server.nats.host", default = "localhost")
val natsPort = conf.get(key = "tts-server.nats.port", default = 4222)
val redisHost = conf.get(key = "tts-server.redis.host", default = "localhost")
val redisPort = conf.get(key = "tts-server.redis.port", default = 6379)
val user = conf.get(key = "tts-server.nats.user", default = "dev")
val password = conf.get(key = "tts-server.nats.password", default = "dev")
val topic = conf.get(key = "tts-server.nats.topic", default = "TTS")
Expand All @@ -24,12 +26,14 @@ object TTSServerSettings {
private fun printDetails(): String {
return """
|
|nats-host = $host
|nats-port = $port
|nats-host = $natsHost
|nats-port = $natsPort
|nats-user = ***
|nats-password = ***
|nats-topic = $topic
|nats-group = $group
|redis-hos = $redisHost
|redis-por = $redisPort
""".replaceIndentByMargin("\t")
}
}
2 changes: 2 additions & 0 deletions app-tts/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ tts-server.nats.user=dev
tts-server.nats.password=dev
tts-server.nats.topic=TTS
tts-server.nats.group=TTS
tts-server.redis.host=localhost
tts-server.redis.port=6379

tts.local.data-directory=classpath:/data
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ bmuschkoVersion=9.4.0
ktorVersion=2.3.12
# https://mvnrepository.com/artifact/io.nats/jnats
natsVersion=2.20.0
# https://mvnrepository.com/artifact/io.lettuce/lettuce-core
lettuceVersion=6.4.0.RELEASE
# https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-serialization-core
kotlinxSerializationVersion=1.7.1
# https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
Expand Down
8 changes: 6 additions & 2 deletions tts-lib/src/main/kotlin/TextToSpeechService.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.gitlab.sszuev.flashcards.speaker

import com.gitlab.sszuev.flashcards.speaker.impl.CaffeineResourceCache
import com.gitlab.sszuev.flashcards.speaker.impl.CombinedTextToSpeechService
import com.gitlab.sszuev.flashcards.speaker.impl.EspeakNgTestToSpeechService
import com.gitlab.sszuev.flashcards.speaker.impl.LocalTextToSpeechService
Expand Down Expand Up @@ -33,10 +34,13 @@ interface TextToSpeechService {
/**
* Creates a [TextToSpeechService].
*/
fun createTTSService(): TextToSpeechService {
fun createTTSService(
cache: ResourceCache = CaffeineResourceCache(),
onGetResource: () -> Unit = {}
): TextToSpeechService {
return if (TTSSettings.ttsServiceVoicerssKey.isNotBlank() && TTSSettings.ttsServiceVoicerssKey != "secret") {
logger.info("::[TTS-SERVICE] init voicerss service")
CombinedTextToSpeechService()
CombinedTextToSpeechService(cache = cache, onGetResource = onGetResource)
} else if (EspeakNgTestToSpeechService.isEspeakNgAvailable()) {
logger.info("::[TTS-SERVICE] init espeak-ng service")
EspeakNgTestToSpeechService()
Expand Down
6 changes: 4 additions & 2 deletions tts-lib/src/main/kotlin/impl/CombinedTextToSpeechService.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package com.gitlab.sszuev.flashcards.speaker.impl

import com.gitlab.sszuev.flashcards.speaker.ResourceCache
import com.gitlab.sszuev.flashcards.speaker.TTSConfig
import com.gitlab.sszuev.flashcards.speaker.TextToSpeechService
import com.gitlab.sszuev.flashcards.speaker.toResourcePath

class CombinedTextToSpeechService(
resourceIdMapper: (String) -> Pair<String, String>? = { toResourcePath(it) },
config: TTSConfig = TTSConfig(),
private val cache: ResourceCache = CaffeineResourceCache(),
private val onGetResource: () -> Unit = {},
) : TextToSpeechService {

private val voicerssTextToSpeechService =
VoicerssTextToSpeechService(resourceIdMapper = resourceIdMapper, config = config)
private val espeakNgTestToSpeechService =
EspeakNgTestToSpeechService(resourceIdMapper = resourceIdMapper, config = config)
private val cache = CaffeineResourceCache()


override suspend fun getResource(id: String, vararg args: String): ByteArray? {
var res = cache.get(id)
Expand All @@ -23,6 +24,7 @@ class CombinedTextToSpeechService(
}
res = voicerssTextToSpeechService.getResource(id, *args)
if (res != null) {
onGetResource()
cache.put(id, res)
return res
}
Expand Down
1 change: 1 addition & 0 deletions tutor-deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The directory contains docker-composer files allowing to set up environment.
- flashcards-db (postgres). it is requires by [:db-pg](../db-pg)
- flashcards-keycloak. Authorization (demo:demo).
- flashcards-nats (transport)
- flashcards-redis (cache)
- flashcards-tts-server
- flashcards-cards-server
- flashcards-dictionaries-server
Expand Down
2 changes: 2 additions & 0 deletions tutor-deploy/data/nats/nats-config.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
port: 4222
max_payload: 5242880
9 changes: 9 additions & 0 deletions tutor-deploy/data/redis/redis.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
maxmemory 100mb
maxmemory-policy allkeys-lru
save 900 1
appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 50
auto-aof-rewrite-min-size 64mb
logfile ""
loglevel notice
26 changes: 21 additions & 5 deletions tutor-deploy/docker-compose-app.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3.9"

networks:
flashcards-net:

Expand Down Expand Up @@ -73,9 +71,8 @@ services:

flashcards-nats:
image: nats:2.10.14-alpine
ports:
- "4222:4222"
- "8222:8222"
volumes:
- "${DATA_DIR}/nats/nats-config.conf:/etc/nats/nats-config.conf"
healthcheck:
test: [ "CMD-SHELL", "netstat -an | grep 4222 | grep LISTEN" ]
interval: 10s
Expand All @@ -84,6 +81,22 @@ services:
start_period: 10s
networks:
- flashcards-net
command: [ "-c", "/etc/nats/nats-config.conf" ]

flashcards-redis:
image: "redis:7.4.0"
volumes:
- "${DATA_DIR}/redis:/data"
- "${DATA_DIR}/redis/redis.conf:/usr/local/etc/redis/redis.conf"
command: [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
restart: unless-stopped
healthcheck:
test: [ "CMD", "redis-cli", "ping" ]
interval: 30s
timeout: 10s
retries: 5
networks:
- flashcards-net

flashcards-tts-server:
image: sszuev/open-tutor-tts-server:2.0.0-snapshot
Expand All @@ -92,8 +105,11 @@ services:
depends_on:
flashcards-nats:
condition: service_healthy
flashcards-redis:
condition: service_healthy
environment:
TTS_SERVER_NATS_HOST: "flashcards-nats"
TTS_SERVER_REDIS_HOST: "flashcards-redis"
TTS_SERVICE_VOICERSS_KEY: "${TTS_SERVICE_VOICERSS_KEY}"

flashcards-cards-server:
Expand Down

0 comments on commit cbb8ba2

Please sign in to comment.