shared event executor group between server and clients
All checks were successful
CI / build (push) Successful in 3m44s
All checks were successful
CI / build (push) Successful in 3m44s
- improved documentation - closed memcache client's thread pools
This commit is contained in:
46
README.md
46
README.md
@@ -66,11 +66,18 @@ buildCache {
|
|||||||
url = 'https://rbcs.example.com/'
|
url = 'https://rbcs.example.com/'
|
||||||
push = true
|
push = true
|
||||||
allowInsecureProtocol = false
|
allowInsecureProtocol = false
|
||||||
|
// The credentials block is only required if you enable
|
||||||
|
// HTTP basic authentication on RBCS
|
||||||
|
credentials {
|
||||||
|
username = 'build-cache-user'
|
||||||
|
password = 'some-complicated-password'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
alternatively you can add this to `${GRADLE_HOME}/init.gradle`
|
alternatively you can add this to `${GRADLE_HOME}/init.gradle` to configure the remote cache
|
||||||
|
at the system level
|
||||||
|
|
||||||
```groovy
|
```groovy
|
||||||
gradle.settingsEvaluated { settings ->
|
gradle.settingsEvaluated { settings ->
|
||||||
@@ -79,14 +86,51 @@ gradle.settingsEvaluated { settings ->
|
|||||||
url = 'https://rbcs.example.com/'
|
url = 'https://rbcs.example.com/'
|
||||||
push = true
|
push = true
|
||||||
allowInsecureProtocol = false
|
allowInsecureProtocol = false
|
||||||
|
// The credentials block is only required if you enable
|
||||||
|
// HTTP basic authentication on RBCS
|
||||||
|
credentials {
|
||||||
|
username = 'build-cache-user'
|
||||||
|
password = 'some-complicated-password'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
add `org.gradle.caching=true` to your `<project>/gradle.properties` or run gradle with `--build-cache`.
|
||||||
|
|
||||||
|
Read [Gradle documentation](https://docs.gradle.org/current/userguide/build_cache.html) for more detailed information.
|
||||||
|
|
||||||
### Using RBCS with Maven
|
### Using RBCS with Maven
|
||||||
|
|
||||||
|
1. Create an `extensions.xml` in `<project>/.mvn/extensions.xml` with the following content
|
||||||
|
```xml
|
||||||
|
<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.1.0 https://maven.apache.org/xsd/core-extensions-1.0.0.xsd">
|
||||||
|
<extension>
|
||||||
|
<groupId>org.apache.maven.extensions</groupId>
|
||||||
|
<artifactId>maven-build-cache-extension</artifactId>
|
||||||
|
<version>1.2.0</version>
|
||||||
|
</extension>
|
||||||
|
</extensions>
|
||||||
|
```
|
||||||
|
2. Copy [maven-build-cache-config.xml](https://maven.apache.org/extensions/maven-build-cache-extension/maven-build-cache-config.xml) into `<project>/.mvn/` folder
|
||||||
|
3. Edit the `cache/configuration/remote` element
|
||||||
|
```xml
|
||||||
|
<remote enabled="true" id="rbcs">
|
||||||
|
<url>https://rbcs.example.com/</url>
|
||||||
|
</remote>
|
||||||
|
```
|
||||||
|
4. Run maven with
|
||||||
|
```bash
|
||||||
|
mvn -Dmaven.build.cache.enabled=true -Dmaven.build.cache.debugOutput=true -Dmaven.build.cache.remote.save.enabled=true package
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively you can set those properties in your `<project>/pom.xml`
|
||||||
|
|
||||||
|
|
||||||
Read [here](https://maven.apache.org/extensions/maven-build-cache-extension/remote-cache.html)
|
Read [here](https://maven.apache.org/extensions/maven-build-cache-extension/remote-cache.html)
|
||||||
|
for more informations
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
### Why should I use a build cache?
|
### Why should I use a build cache?
|
||||||
|
@@ -14,9 +14,7 @@ allprojects { subproject ->
|
|||||||
if(project.currentTag.isPresent()) {
|
if(project.currentTag.isPresent()) {
|
||||||
version = project.currentTag.map { it[0] }.get()
|
version = project.currentTag.map { it[0] }.get()
|
||||||
} else {
|
} else {
|
||||||
version = project.gitRevision.map { gitRevision ->
|
version = "${getProperty('rbcs.version')}-SNAPSHOT"
|
||||||
"${getProperty('rbcs.version')}.${gitRevision[0..10]}"
|
|
||||||
}.get()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@@ -24,7 +22,6 @@ allprojects { subproject ->
|
|||||||
url = getProperty('gitea.maven.url')
|
url = getProperty('gitea.maven.url')
|
||||||
content {
|
content {
|
||||||
includeModule 'net.woggioni', 'jwo'
|
includeModule 'net.woggioni', 'jwo'
|
||||||
includeModule 'net.woggioni', 'xmemcached'
|
|
||||||
includeGroup 'com.lys'
|
includeGroup 'com.lys'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
api catalog.netty.common
|
||||||
api catalog.netty.buffer
|
api catalog.netty.buffer
|
||||||
api catalog.netty.handler
|
api catalog.netty.handler
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ module net.woggioni.rbcs.api {
|
|||||||
requires io.netty.buffer;
|
requires io.netty.buffer;
|
||||||
requires io.netty.handler;
|
requires io.netty.handler;
|
||||||
requires io.netty.transport;
|
requires io.netty.transport;
|
||||||
|
requires io.netty.common;
|
||||||
exports net.woggioni.rbcs.api;
|
exports net.woggioni.rbcs.api;
|
||||||
exports net.woggioni.rbcs.api.exception;
|
exports net.woggioni.rbcs.api.exception;
|
||||||
exports net.woggioni.rbcs.api.message;
|
exports net.woggioni.rbcs.api.message;
|
||||||
|
@@ -0,0 +1,13 @@
|
|||||||
|
package net.woggioni.rbcs.api;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
public interface AsyncCloseable extends AutoCloseable {
|
||||||
|
|
||||||
|
CompletableFuture<Void> asyncClose();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void close() throws Exception {
|
||||||
|
asyncClose().get();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,15 @@
|
|||||||
package net.woggioni.rbcs.api;
|
package net.woggioni.rbcs.api;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelFactory;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
|
import io.netty.channel.EventLoopGroup;
|
||||||
|
import io.netty.channel.socket.DatagramChannel;
|
||||||
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
|
||||||
public interface CacheHandlerFactory extends AutoCloseable {
|
public interface CacheHandlerFactory extends AsyncCloseable {
|
||||||
ChannelHandler newHandler();
|
ChannelHandler newHandler(
|
||||||
|
EventLoopGroup eventLoopGroup,
|
||||||
|
ChannelFactory<SocketChannel> socketChannelFactory,
|
||||||
|
ChannelFactory<DatagramChannel> datagramChannelFactory
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@@ -83,17 +83,6 @@ public class Configuration {
|
|||||||
Group extract(X509Certificate cert);
|
Group extract(X509Certificate cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Value
|
|
||||||
public static class Throttling {
|
|
||||||
KeyStore keyStore;
|
|
||||||
TrustStore trustStore;
|
|
||||||
boolean verifyClients;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ClientCertificate {
|
|
||||||
REQUIRED, OPTIONAL
|
|
||||||
}
|
|
||||||
|
|
||||||
@Value
|
@Value
|
||||||
public static class Tls {
|
public static class Tls {
|
||||||
KeyStore keyStore;
|
KeyStore keyStore;
|
||||||
|
@@ -9,6 +9,7 @@ plugins {
|
|||||||
id 'maven-publish'
|
id 'maven-publish'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import net.woggioni.gradle.envelope.EnvelopePlugin
|
||||||
import net.woggioni.gradle.envelope.EnvelopeJarTask
|
import net.woggioni.gradle.envelope.EnvelopeJarTask
|
||||||
import net.woggioni.gradle.graalvm.NativeImageConfigurationTask
|
import net.woggioni.gradle.graalvm.NativeImageConfigurationTask
|
||||||
import net.woggioni.gradle.graalvm.NativeImagePlugin
|
import net.woggioni.gradle.graalvm.NativeImagePlugin
|
||||||
@@ -16,15 +17,6 @@ import net.woggioni.gradle.graalvm.NativeImageTask
|
|||||||
import net.woggioni.gradle.graalvm.JlinkPlugin
|
import net.woggioni.gradle.graalvm.JlinkPlugin
|
||||||
import net.woggioni.gradle.graalvm.JlinkTask
|
import net.woggioni.gradle.graalvm.JlinkTask
|
||||||
|
|
||||||
Property<String> mainModuleName = objects.property(String.class)
|
|
||||||
mainModuleName.set('net.woggioni.rbcs.cli')
|
|
||||||
Property<String> mainClassName = objects.property(String.class)
|
|
||||||
mainClassName.set('net.woggioni.rbcs.cli.RemoteBuildCacheServerCli')
|
|
||||||
|
|
||||||
tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile) {
|
|
||||||
options.javaModuleMainClass = mainClassName
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
release {
|
release {
|
||||||
transitive = false
|
transitive = false
|
||||||
@@ -34,13 +26,6 @@ configurations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
envelopeJar {
|
|
||||||
mainModule = mainModuleName
|
|
||||||
mainClass = mainClassName
|
|
||||||
|
|
||||||
extraClasspath = ["plugins"]
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation catalog.jwo
|
implementation catalog.jwo
|
||||||
implementation catalog.slf4j.api
|
implementation catalog.slf4j.api
|
||||||
@@ -54,18 +39,24 @@ dependencies {
|
|||||||
// runtimeOnly catalog.slf4j.simple
|
// runtimeOnly catalog.slf4j.simple
|
||||||
}
|
}
|
||||||
|
|
||||||
Provider<EnvelopeJarTask> envelopeJarTaskProvider = tasks.named('envelopeJar', EnvelopeJarTask.class) {
|
|
||||||
// systemProperties['java.util.logging.config.class'] = 'net.woggioni.rbcs.LoggingConfig'
|
Property<String> mainModuleName = objects.property(String.class)
|
||||||
// systemProperties['log.config.source'] = 'net/woggioni/rbcs/cli/logging.properties'
|
mainModuleName.set('net.woggioni.rbcs.cli')
|
||||||
// systemProperties['java.util.logging.config.file'] = 'classpath:net/woggioni/rbcs/cli/logging.properties'
|
Property<String> mainClassName = objects.property(String.class)
|
||||||
|
mainClassName.set('net.woggioni.rbcs.cli.RemoteBuildCacheServerCli')
|
||||||
|
|
||||||
|
tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile) {
|
||||||
|
options.javaModuleMainClass = mainClassName
|
||||||
|
}
|
||||||
|
|
||||||
|
Provider<EnvelopeJarTask> envelopeJarTaskProvider = tasks.named(EnvelopePlugin.ENVELOPE_JAR_TASK_NAME, EnvelopeJarTask.class) {
|
||||||
|
mainModule = mainModuleName
|
||||||
|
mainClass = mainClassName
|
||||||
|
|
||||||
|
extraClasspath = ["plugins"]
|
||||||
|
|
||||||
systemProperties['logback.configurationFile'] = 'classpath:net/woggioni/rbcs/cli/logback.xml'
|
systemProperties['logback.configurationFile'] = 'classpath:net/woggioni/rbcs/cli/logback.xml'
|
||||||
systemProperties['io.netty.leakDetectionLevel'] = 'DISABLED'
|
systemProperties['io.netty.leakDetectionLevel'] = 'DISABLED'
|
||||||
|
|
||||||
// systemProperties['org.slf4j.simpleLogger.showDateTime'] = 'true'
|
|
||||||
// systemProperties['org.slf4j.simpleLogger.defaultLogLevel'] = 'debug'
|
|
||||||
// systemProperties['org.slf4j.simpleLogger.log.com.google.code.yanf4j'] = 'warn'
|
|
||||||
// systemProperties['org.slf4j.simpleLogger.log.net.rubyeye.xmemcached'] = 'warn'
|
|
||||||
// systemProperties['org.slf4j.simpleLogger.dateTimeFormat'] = 'yyyy-MM-dd\'T\'HH:mm:ss.SSSZ'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named(NativeImagePlugin.CONFIGURE_NATIVE_IMAGE_TASK_NAME, NativeImageConfigurationTask) {
|
tasks.named(NativeImagePlugin.CONFIGURE_NATIVE_IMAGE_TASK_NAME, NativeImageConfigurationTask) {
|
||||||
|
@@ -32,7 +32,7 @@ object RBCS {
|
|||||||
|
|
||||||
fun digest(
|
fun digest(
|
||||||
data: ByteArray,
|
data: ByteArray,
|
||||||
md: MessageDigest = MessageDigest.getInstance("MD5")
|
md: MessageDigest
|
||||||
): ByteArray {
|
): ByteArray {
|
||||||
md.update(data)
|
md.update(data)
|
||||||
return md.digest()
|
return md.digest()
|
||||||
@@ -40,7 +40,7 @@ object RBCS {
|
|||||||
|
|
||||||
fun digestString(
|
fun digestString(
|
||||||
data: ByteArray,
|
data: ByteArray,
|
||||||
md: MessageDigest = MessageDigest.getInstance("MD5")
|
md: MessageDigest
|
||||||
): String {
|
): String {
|
||||||
return JWO.bytesToHex(digest(data, md))
|
return JWO.bytesToHex(digest(data, md))
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,20 @@
|
|||||||
package net.woggioni.rbcs.server.memcache
|
package net.woggioni.rbcs.server.memcache
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelFactory
|
||||||
|
import io.netty.channel.ChannelHandler
|
||||||
|
import io.netty.channel.EventLoopGroup
|
||||||
|
import io.netty.channel.pool.FixedChannelPool
|
||||||
|
import io.netty.channel.socket.DatagramChannel
|
||||||
|
import io.netty.channel.socket.SocketChannel
|
||||||
import net.woggioni.rbcs.api.CacheHandlerFactory
|
import net.woggioni.rbcs.api.CacheHandlerFactory
|
||||||
import net.woggioni.rbcs.api.Configuration
|
import net.woggioni.rbcs.api.Configuration
|
||||||
import net.woggioni.rbcs.common.HostAndPort
|
import net.woggioni.rbcs.common.HostAndPort
|
||||||
import net.woggioni.rbcs.server.memcache.client.MemcacheClient
|
import net.woggioni.rbcs.server.memcache.client.MemcacheClient
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
data class MemcacheCacheConfiguration(
|
data class MemcacheCacheConfiguration(
|
||||||
val servers: List<Server>,
|
val servers: List<Server>,
|
||||||
@@ -12,7 +22,7 @@ data class MemcacheCacheConfiguration(
|
|||||||
val digestAlgorithm: String? = null,
|
val digestAlgorithm: String? = null,
|
||||||
val compressionMode: CompressionMode? = null,
|
val compressionMode: CompressionMode? = null,
|
||||||
val compressionLevel: Int,
|
val compressionLevel: Int,
|
||||||
val chunkSize : Int
|
val chunkSize: Int
|
||||||
) : Configuration.Cache {
|
) : Configuration.Cache {
|
||||||
|
|
||||||
enum class CompressionMode {
|
enum class CompressionMode {
|
||||||
@@ -23,19 +33,58 @@ data class MemcacheCacheConfiguration(
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class Server(
|
data class Server(
|
||||||
val endpoint : HostAndPort,
|
val endpoint: HostAndPort,
|
||||||
val connectionTimeoutMillis : Int?,
|
val connectionTimeoutMillis: Int?,
|
||||||
val maxConnections : Int
|
val maxConnections: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
override fun materialize() = object : CacheHandlerFactory {
|
override fun materialize() = object : CacheHandlerFactory {
|
||||||
private val client = MemcacheClient(this@MemcacheCacheConfiguration.servers, chunkSize)
|
|
||||||
override fun close() {
|
private val connectionPoolMap = ConcurrentHashMap<HostAndPort, FixedChannelPool>()
|
||||||
client.close()
|
|
||||||
|
override fun newHandler(
|
||||||
|
eventLoop: EventLoopGroup,
|
||||||
|
socketChannelFactory: ChannelFactory<SocketChannel>,
|
||||||
|
datagramChannelFactory: ChannelFactory<DatagramChannel>
|
||||||
|
): ChannelHandler {
|
||||||
|
return MemcacheCacheHandler(
|
||||||
|
MemcacheClient(
|
||||||
|
this@MemcacheCacheConfiguration.servers,
|
||||||
|
chunkSize,
|
||||||
|
eventLoop,
|
||||||
|
socketChannelFactory,
|
||||||
|
connectionPoolMap
|
||||||
|
),
|
||||||
|
digestAlgorithm,
|
||||||
|
compressionMode != null,
|
||||||
|
compressionLevel,
|
||||||
|
chunkSize,
|
||||||
|
maxAge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun asyncClose() = object : CompletableFuture<Void>() {
|
||||||
|
init {
|
||||||
|
val failure = AtomicReference<Throwable>(null)
|
||||||
|
val pools = connectionPoolMap.values.toList()
|
||||||
|
val npools = pools.size
|
||||||
|
val finished = AtomicInteger(0)
|
||||||
|
pools.forEach { pool ->
|
||||||
|
pool.closeAsync().addListener {
|
||||||
|
if (!it.isSuccess) {
|
||||||
|
failure.compareAndSet(null, it.cause())
|
||||||
|
}
|
||||||
|
if(finished.incrementAndGet() == npools) {
|
||||||
|
when(val ex = failure.get()) {
|
||||||
|
null -> complete(null)
|
||||||
|
else -> completeExceptionally(ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun newHandler() = MemcacheCacheHandler(client, digestAlgorithm, compressionMode != null, compressionLevel, chunkSize, maxAge)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNamespaceURI() = "urn:net.woggioni.rbcs.server.memcache"
|
override fun getNamespaceURI() = "urn:net.woggioni.rbcs.server.memcache"
|
||||||
|
@@ -4,16 +4,17 @@ package net.woggioni.rbcs.server.memcache.client
|
|||||||
import io.netty.bootstrap.Bootstrap
|
import io.netty.bootstrap.Bootstrap
|
||||||
import io.netty.buffer.ByteBuf
|
import io.netty.buffer.ByteBuf
|
||||||
import io.netty.channel.Channel
|
import io.netty.channel.Channel
|
||||||
|
import io.netty.channel.ChannelFactory
|
||||||
import io.netty.channel.ChannelFutureListener
|
import io.netty.channel.ChannelFutureListener
|
||||||
import io.netty.channel.ChannelHandlerContext
|
import io.netty.channel.ChannelHandlerContext
|
||||||
import io.netty.channel.ChannelOption
|
import io.netty.channel.ChannelOption
|
||||||
import io.netty.channel.ChannelPipeline
|
import io.netty.channel.ChannelPipeline
|
||||||
|
import io.netty.channel.EventLoopGroup
|
||||||
import io.netty.channel.SimpleChannelInboundHandler
|
import io.netty.channel.SimpleChannelInboundHandler
|
||||||
import io.netty.channel.nio.NioEventLoopGroup
|
|
||||||
import io.netty.channel.pool.AbstractChannelPoolHandler
|
import io.netty.channel.pool.AbstractChannelPoolHandler
|
||||||
import io.netty.channel.pool.ChannelPool
|
import io.netty.channel.pool.ChannelPool
|
||||||
import io.netty.channel.pool.FixedChannelPool
|
import io.netty.channel.pool.FixedChannelPool
|
||||||
import io.netty.channel.socket.nio.NioSocketChannel
|
import io.netty.channel.socket.SocketChannel
|
||||||
import io.netty.handler.codec.memcache.LastMemcacheContent
|
import io.netty.handler.codec.memcache.LastMemcacheContent
|
||||||
import io.netty.handler.codec.memcache.MemcacheContent
|
import io.netty.handler.codec.memcache.MemcacheContent
|
||||||
import io.netty.handler.codec.memcache.MemcacheObject
|
import io.netty.handler.codec.memcache.MemcacheObject
|
||||||
@@ -33,23 +34,22 @@ import java.util.concurrent.ConcurrentHashMap
|
|||||||
import io.netty.util.concurrent.Future as NettyFuture
|
import io.netty.util.concurrent.Future as NettyFuture
|
||||||
|
|
||||||
|
|
||||||
class MemcacheClient(private val servers: List<MemcacheCacheConfiguration.Server>, private val chunkSize : Int) : AutoCloseable {
|
class MemcacheClient(
|
||||||
|
private val servers: List<MemcacheCacheConfiguration.Server>,
|
||||||
|
private val chunkSize : Int,
|
||||||
|
private val group: EventLoopGroup,
|
||||||
|
private val channelFactory: ChannelFactory<SocketChannel>,
|
||||||
|
private val connectionPool: ConcurrentHashMap<HostAndPort, FixedChannelPool>
|
||||||
|
) : AutoCloseable {
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
private val log = createLogger<MemcacheCacheHandler>()
|
private val log = createLogger<MemcacheCacheHandler>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val group: NioEventLoopGroup
|
|
||||||
private val connectionPool: MutableMap<HostAndPort, ChannelPool> = ConcurrentHashMap()
|
|
||||||
|
|
||||||
init {
|
|
||||||
group = NioEventLoopGroup()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun newConnectionPool(server: MemcacheCacheConfiguration.Server): FixedChannelPool {
|
private fun newConnectionPool(server: MemcacheCacheConfiguration.Server): FixedChannelPool {
|
||||||
val bootstrap = Bootstrap().apply {
|
val bootstrap = Bootstrap().apply {
|
||||||
group(group)
|
group(group)
|
||||||
channel(NioSocketChannel::class.java)
|
channelFactory(channelFactory)
|
||||||
option(ChannelOption.SO_KEEPALIVE, true)
|
option(ChannelOption.SO_KEEPALIVE, true)
|
||||||
remoteAddress(InetSocketAddress(server.endpoint.host, server.endpoint.port))
|
remoteAddress(InetSocketAddress(server.endpoint.host, server.endpoint.port))
|
||||||
server.connectionTimeoutMillis?.let {
|
server.connectionTimeoutMillis?.let {
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
<xs:attribute name="max-age" type="xs:duration" default="P1D"/>
|
<xs:attribute name="max-age" type="xs:duration" default="P1D"/>
|
||||||
<xs:attribute name="chunk-size" type="rbcs:byteSizeType" default="0x10000"/>
|
<xs:attribute name="chunk-size" type="rbcs:byteSizeType" default="0x10000"/>
|
||||||
<xs:attribute name="digest" type="xs:token" />
|
<xs:attribute name="digest" type="xs:token"/>
|
||||||
<xs:attribute name="compression-mode" type="rbcs-memcache:compressionType"/>
|
<xs:attribute name="compression-mode" type="rbcs-memcache:compressionType"/>
|
||||||
<xs:attribute name="compression-level" type="rbcs:compressionLevelType" default="-1"/>
|
<xs:attribute name="compression-level" type="rbcs:compressionLevelType" default="-1"/>
|
||||||
</xs:extension>
|
</xs:extension>
|
||||||
|
@@ -3,6 +3,7 @@ package net.woggioni.rbcs.server
|
|||||||
import io.netty.bootstrap.ServerBootstrap
|
import io.netty.bootstrap.ServerBootstrap
|
||||||
import io.netty.buffer.ByteBuf
|
import io.netty.buffer.ByteBuf
|
||||||
import io.netty.channel.Channel
|
import io.netty.channel.Channel
|
||||||
|
import io.netty.channel.ChannelFactory
|
||||||
import io.netty.channel.ChannelFuture
|
import io.netty.channel.ChannelFuture
|
||||||
import io.netty.channel.ChannelHandler.Sharable
|
import io.netty.channel.ChannelHandler.Sharable
|
||||||
import io.netty.channel.ChannelHandlerContext
|
import io.netty.channel.ChannelHandlerContext
|
||||||
@@ -11,7 +12,12 @@ import io.netty.channel.ChannelInitializer
|
|||||||
import io.netty.channel.ChannelOption
|
import io.netty.channel.ChannelOption
|
||||||
import io.netty.channel.ChannelPromise
|
import io.netty.channel.ChannelPromise
|
||||||
import io.netty.channel.nio.NioEventLoopGroup
|
import io.netty.channel.nio.NioEventLoopGroup
|
||||||
|
import io.netty.channel.socket.DatagramChannel
|
||||||
|
import io.netty.channel.socket.ServerSocketChannel
|
||||||
|
import io.netty.channel.socket.SocketChannel
|
||||||
|
import io.netty.channel.socket.nio.NioDatagramChannel
|
||||||
import io.netty.channel.socket.nio.NioServerSocketChannel
|
import io.netty.channel.socket.nio.NioServerSocketChannel
|
||||||
|
import io.netty.channel.socket.nio.NioSocketChannel
|
||||||
import io.netty.handler.codec.compression.CompressionOptions
|
import io.netty.handler.codec.compression.CompressionOptions
|
||||||
import io.netty.handler.codec.http.DefaultHttpContent
|
import io.netty.handler.codec.http.DefaultHttpContent
|
||||||
import io.netty.handler.codec.http.HttpContentCompressor
|
import io.netty.handler.codec.http.HttpContentCompressor
|
||||||
@@ -31,6 +37,7 @@ import io.netty.util.concurrent.DefaultEventExecutorGroup
|
|||||||
import io.netty.util.concurrent.EventExecutorGroup
|
import io.netty.util.concurrent.EventExecutorGroup
|
||||||
import net.woggioni.jwo.JWO
|
import net.woggioni.jwo.JWO
|
||||||
import net.woggioni.jwo.Tuple2
|
import net.woggioni.jwo.Tuple2
|
||||||
|
import net.woggioni.rbcs.api.AsyncCloseable
|
||||||
import net.woggioni.rbcs.api.Configuration
|
import net.woggioni.rbcs.api.Configuration
|
||||||
import net.woggioni.rbcs.api.exception.ConfigurationException
|
import net.woggioni.rbcs.api.exception.ConfigurationException
|
||||||
import net.woggioni.rbcs.common.PasswordSecurity.decodePasswordHash
|
import net.woggioni.rbcs.common.PasswordSecurity.decodePasswordHash
|
||||||
@@ -200,8 +207,10 @@ class RemoteBuildCacheServer(private val cfg: Configuration) {
|
|||||||
|
|
||||||
private class ServerInitializer(
|
private class ServerInitializer(
|
||||||
private val cfg: Configuration,
|
private val cfg: Configuration,
|
||||||
|
private val channelFactory : ChannelFactory<SocketChannel>,
|
||||||
|
private val datagramChannelFactory : ChannelFactory<DatagramChannel>,
|
||||||
private val eventExecutorGroup: EventExecutorGroup
|
private val eventExecutorGroup: EventExecutorGroup
|
||||||
) : ChannelInitializer<Channel>(), AutoCloseable {
|
) : ChannelInitializer<Channel>(), AsyncCloseable {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun createSslCtx(tls: Configuration.Tls): SslContext {
|
private fun createSslCtx(tls: Configuration.Tls): SslContext {
|
||||||
@@ -368,21 +377,20 @@ class RemoteBuildCacheServer(private val cfg: Configuration) {
|
|||||||
ServerHandler(prefix)
|
ServerHandler(prefix)
|
||||||
}
|
}
|
||||||
pipeline.addLast(eventExecutorGroup, ServerHandler.NAME, serverHandler)
|
pipeline.addLast(eventExecutorGroup, ServerHandler.NAME, serverHandler)
|
||||||
pipeline.addLast(cacheHandlerFactory.newHandler())
|
|
||||||
|
pipeline.addLast(cacheHandlerFactory.newHandler(ch.eventLoop(), channelFactory, datagramChannelFactory))
|
||||||
pipeline.addLast(TraceHandler)
|
pipeline.addLast(TraceHandler)
|
||||||
pipeline.addLast(ExceptionHandler)
|
pipeline.addLast(ExceptionHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun asyncClose() = cacheHandlerFactory.asyncClose()
|
||||||
cacheHandlerFactory.close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerHandle(
|
class ServerHandle(
|
||||||
closeFuture: ChannelFuture,
|
closeFuture: ChannelFuture,
|
||||||
private val bossGroup: EventExecutorGroup,
|
private val bossGroup: EventExecutorGroup,
|
||||||
private val executorGroups: Iterable<EventExecutorGroup>,
|
private val executorGroups: Iterable<EventExecutorGroup>,
|
||||||
private val serverInitializer: AutoCloseable,
|
private val serverInitializer: AsyncCloseable,
|
||||||
) : Future<Void> by from(closeFuture, executorGroups, serverInitializer) {
|
) : Future<Void> by from(closeFuture, executorGroups, serverInitializer) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -391,42 +399,53 @@ class RemoteBuildCacheServer(private val cfg: Configuration) {
|
|||||||
private fun from(
|
private fun from(
|
||||||
closeFuture: ChannelFuture,
|
closeFuture: ChannelFuture,
|
||||||
executorGroups: Iterable<EventExecutorGroup>,
|
executorGroups: Iterable<EventExecutorGroup>,
|
||||||
serverInitializer: AutoCloseable
|
serverInitializer: AsyncCloseable
|
||||||
): CompletableFuture<Void> {
|
): CompletableFuture<Void> {
|
||||||
val result = CompletableFuture<Void>()
|
val result = CompletableFuture<Void>()
|
||||||
closeFuture.addListener {
|
closeFuture.addListener {
|
||||||
val errors = mutableListOf<Throwable>()
|
val errors = mutableListOf<Throwable>()
|
||||||
val deadline = Instant.now().plusSeconds(20)
|
val deadline = Instant.now().plusSeconds(20)
|
||||||
|
|
||||||
|
|
||||||
for (executorGroup in executorGroups) {
|
|
||||||
val future = executorGroup.terminationFuture()
|
|
||||||
try {
|
|
||||||
val now = Instant.now()
|
|
||||||
if (now > deadline) {
|
|
||||||
future.get(0, TimeUnit.SECONDS)
|
|
||||||
} else {
|
|
||||||
future.get(Duration.between(now, deadline).toMillis(), TimeUnit.MILLISECONDS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (te: TimeoutException) {
|
|
||||||
errors.addLast(te)
|
|
||||||
log.warn("Timeout while waiting for shutdown of $executorGroup", te)
|
|
||||||
} catch (ex: Throwable) {
|
|
||||||
log.warn(ex.message, ex)
|
|
||||||
errors.addLast(ex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
serverInitializer.close()
|
serverInitializer.close()
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
log.error(ex.message, ex)
|
log.error(ex.message, ex)
|
||||||
errors.addLast(ex)
|
errors.addLast(ex)
|
||||||
}
|
}
|
||||||
if(errors.isEmpty()) {
|
|
||||||
result.complete(null)
|
serverInitializer.asyncClose().whenComplete { _, ex ->
|
||||||
} else {
|
if(ex != null) {
|
||||||
result.completeExceptionally(errors.first())
|
log.error(ex.message, ex)
|
||||||
|
errors.addLast(ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
executorGroups.map {
|
||||||
|
it.shutdownGracefully()
|
||||||
|
}
|
||||||
|
|
||||||
|
for (executorGroup in executorGroups) {
|
||||||
|
val future = executorGroup.terminationFuture()
|
||||||
|
try {
|
||||||
|
val now = Instant.now()
|
||||||
|
if (now > deadline) {
|
||||||
|
future.get(0, TimeUnit.SECONDS)
|
||||||
|
} else {
|
||||||
|
future.get(Duration.between(now, deadline).toMillis(), TimeUnit.MILLISECONDS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (te: TimeoutException) {
|
||||||
|
errors.addLast(te)
|
||||||
|
log.warn("Timeout while waiting for shutdown of $executorGroup", te)
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
log.warn(ex.message, ex)
|
||||||
|
errors.addLast(ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(errors.isEmpty()) {
|
||||||
|
result.complete(null)
|
||||||
|
} else {
|
||||||
|
result.completeExceptionally(errors.first())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,16 +460,15 @@ class RemoteBuildCacheServer(private val cfg: Configuration) {
|
|||||||
|
|
||||||
fun sendShutdownSignal() {
|
fun sendShutdownSignal() {
|
||||||
bossGroup.shutdownGracefully()
|
bossGroup.shutdownGracefully()
|
||||||
executorGroups.map {
|
|
||||||
it.shutdownGracefully()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun run(): ServerHandle {
|
fun run(): ServerHandle {
|
||||||
// Create the multithreaded event loops for the server
|
// Create the multithreaded event loops for the server
|
||||||
val bossGroup = NioEventLoopGroup(1)
|
val bossGroup = NioEventLoopGroup(1)
|
||||||
val serverSocketChannel = NioServerSocketChannel::class.java
|
val channelFactory = ChannelFactory<SocketChannel> { NioSocketChannel() }
|
||||||
|
val datagramChannelFactory = ChannelFactory<DatagramChannel> { NioDatagramChannel() }
|
||||||
|
val serverChannelFactory = ChannelFactory<ServerSocketChannel> { NioServerSocketChannel() }
|
||||||
val workerGroup = NioEventLoopGroup(0)
|
val workerGroup = NioEventLoopGroup(0)
|
||||||
val eventExecutorGroup = run {
|
val eventExecutorGroup = run {
|
||||||
val threadFactory = if (cfg.eventExecutor.isUseVirtualThreads) {
|
val threadFactory = if (cfg.eventExecutor.isUseVirtualThreads) {
|
||||||
@@ -460,11 +478,11 @@ class RemoteBuildCacheServer(private val cfg: Configuration) {
|
|||||||
}
|
}
|
||||||
DefaultEventExecutorGroup(Runtime.getRuntime().availableProcessors(), threadFactory)
|
DefaultEventExecutorGroup(Runtime.getRuntime().availableProcessors(), threadFactory)
|
||||||
}
|
}
|
||||||
val serverInitializer = ServerInitializer(cfg, eventExecutorGroup)
|
val serverInitializer = ServerInitializer(cfg, channelFactory, datagramChannelFactory, workerGroup)
|
||||||
val bootstrap = ServerBootstrap().apply {
|
val bootstrap = ServerBootstrap().apply {
|
||||||
// Configure the server
|
// Configure the server
|
||||||
group(bossGroup, workerGroup)
|
group(bossGroup, workerGroup)
|
||||||
channel(serverSocketChannel)
|
channelFactory(serverChannelFactory)
|
||||||
childHandler(serverInitializer)
|
childHandler(serverInitializer)
|
||||||
option(ChannelOption.SO_BACKLOG, cfg.incomingConnectionsBacklogSize)
|
option(ChannelOption.SO_BACKLOG, cfg.incomingConnectionsBacklogSize)
|
||||||
childOption(ChannelOption.SO_KEEPALIVE, true)
|
childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package net.woggioni.rbcs.server.cache
|
package net.woggioni.rbcs.server.cache
|
||||||
|
|
||||||
import net.woggioni.jwo.JWO
|
import net.woggioni.jwo.JWO
|
||||||
|
import net.woggioni.rbcs.api.AsyncCloseable
|
||||||
import net.woggioni.rbcs.api.CacheValueMetadata
|
import net.woggioni.rbcs.api.CacheValueMetadata
|
||||||
import net.woggioni.rbcs.common.createLogger
|
import net.woggioni.rbcs.common.createLogger
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
@@ -18,11 +19,12 @@ import java.nio.file.StandardOpenOption
|
|||||||
import java.nio.file.attribute.BasicFileAttributes
|
import java.nio.file.attribute.BasicFileAttributes
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
class FileSystemCache(
|
class FileSystemCache(
|
||||||
val root: Path,
|
val root: Path,
|
||||||
val maxAge: Duration
|
val maxAge: Duration
|
||||||
) : AutoCloseable {
|
) : AsyncCloseable {
|
||||||
|
|
||||||
class EntryValue(val metadata: CacheValueMetadata, val channel : FileChannel, val offset : Long, val size : Long) : Serializable
|
class EntryValue(val metadata: CacheValueMetadata, val channel : FileChannel, val offset : Long, val size : Long) : Serializable
|
||||||
|
|
||||||
@@ -112,9 +114,18 @@ class FileSystemCache(
|
|||||||
return FileSink(metadata, file, tmpFile)
|
return FileSink(metadata, file, tmpFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val garbageCollector = Thread.ofVirtual().name("file-system-cache-gc").start {
|
private val closeFuture = object : CompletableFuture<Void>() {
|
||||||
while (running) {
|
init {
|
||||||
gc()
|
Thread.ofVirtual().name("file-system-cache-gc").start {
|
||||||
|
try {
|
||||||
|
while (running) {
|
||||||
|
gc()
|
||||||
|
}
|
||||||
|
complete(null)
|
||||||
|
} catch (ex : Throwable) {
|
||||||
|
completeExceptionally(ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,8 +162,8 @@ class FileSystemCache(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun asyncClose() : CompletableFuture<Void> {
|
||||||
running = false
|
running = false
|
||||||
garbageCollector.join()
|
return closeFuture
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,9 @@
|
|||||||
package net.woggioni.rbcs.server.cache
|
package net.woggioni.rbcs.server.cache
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelFactory
|
||||||
|
import io.netty.channel.EventLoopGroup
|
||||||
|
import io.netty.channel.socket.DatagramChannel
|
||||||
|
import io.netty.channel.socket.SocketChannel
|
||||||
import net.woggioni.jwo.Application
|
import net.woggioni.jwo.Application
|
||||||
import net.woggioni.rbcs.api.CacheHandlerFactory
|
import net.woggioni.rbcs.api.CacheHandlerFactory
|
||||||
import net.woggioni.rbcs.api.Configuration
|
import net.woggioni.rbcs.api.Configuration
|
||||||
@@ -19,11 +23,13 @@ data class FileSystemCacheConfiguration(
|
|||||||
override fun materialize() = object : CacheHandlerFactory {
|
override fun materialize() = object : CacheHandlerFactory {
|
||||||
private val cache = FileSystemCache(root ?: Application.builder("rbcs").build().computeCacheDirectory(), maxAge)
|
private val cache = FileSystemCache(root ?: Application.builder("rbcs").build().computeCacheDirectory(), maxAge)
|
||||||
|
|
||||||
override fun close() {
|
override fun asyncClose() = cache.asyncClose()
|
||||||
cache.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun newHandler() = FileSystemCacheHandler(cache, digestAlgorithm, compressionEnabled, compressionLevel, chunkSize)
|
override fun newHandler(
|
||||||
|
eventLoop: EventLoopGroup,
|
||||||
|
socketChannelFactory: ChannelFactory<SocketChannel>,
|
||||||
|
datagramChannelFactory: ChannelFactory<DatagramChannel>
|
||||||
|
) = FileSystemCacheHandler(cache, digestAlgorithm, compressionEnabled, compressionLevel, chunkSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNamespaceURI() = RBCS.RBCS_NAMESPACE_URI
|
override fun getNamespaceURI() = RBCS.RBCS_NAMESPACE_URI
|
||||||
|
@@ -30,7 +30,7 @@ class FileSystemCacheProvider : CacheProvider<FileSystemCacheConfiguration> {
|
|||||||
val compressionLevel = el.renderAttribute("compression-level")
|
val compressionLevel = el.renderAttribute("compression-level")
|
||||||
?.let(String::toInt)
|
?.let(String::toInt)
|
||||||
?: Deflater.DEFAULT_COMPRESSION
|
?: Deflater.DEFAULT_COMPRESSION
|
||||||
val digestAlgorithm = el.renderAttribute("digest") ?: "MD5"
|
val digestAlgorithm = el.renderAttribute("digest")
|
||||||
val chunkSize = el.renderAttribute("chunk-size")
|
val chunkSize = el.renderAttribute("chunk-size")
|
||||||
?.let(Integer::decode)
|
?.let(Integer::decode)
|
||||||
?: 0x10000
|
?: 0x10000
|
||||||
|
@@ -1,10 +1,12 @@
|
|||||||
package net.woggioni.rbcs.server.cache
|
package net.woggioni.rbcs.server.cache
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf
|
import io.netty.buffer.ByteBuf
|
||||||
|
import net.woggioni.rbcs.api.AsyncCloseable
|
||||||
import net.woggioni.rbcs.api.CacheValueMetadata
|
import net.woggioni.rbcs.api.CacheValueMetadata
|
||||||
import net.woggioni.rbcs.common.createLogger
|
import net.woggioni.rbcs.common.createLogger
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.PriorityBlockingQueue
|
import java.util.concurrent.PriorityBlockingQueue
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@@ -26,7 +28,7 @@ class CacheEntry(
|
|||||||
class InMemoryCache(
|
class InMemoryCache(
|
||||||
private val maxAge: Duration,
|
private val maxAge: Duration,
|
||||||
private val maxSize: Long
|
private val maxSize: Long
|
||||||
) : AutoCloseable {
|
) : AsyncCloseable {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = createLogger<InMemoryCache>()
|
private val log = createLogger<InMemoryCache>()
|
||||||
@@ -45,26 +47,35 @@ class InMemoryCache(
|
|||||||
@Volatile
|
@Volatile
|
||||||
private var running = true
|
private var running = true
|
||||||
|
|
||||||
private val garbageCollector = Thread.ofVirtual().name("in-memory-cache-gc").start {
|
private val closeFuture = object : CompletableFuture<Void>() {
|
||||||
while (running) {
|
init {
|
||||||
val el = removalQueue.poll(1, TimeUnit.SECONDS) ?: continue
|
Thread.ofVirtual().name("in-memory-cache-gc").start {
|
||||||
val value = el.value
|
try {
|
||||||
val now = Instant.now()
|
while (running) {
|
||||||
if (now > el.expiry) {
|
val el = removalQueue.poll(1, TimeUnit.SECONDS) ?: continue
|
||||||
val removed = map.remove(el.key, value)
|
val value = el.value
|
||||||
if (removed) {
|
val now = Instant.now()
|
||||||
updateSizeAfterRemoval(value.content)
|
if (now > el.expiry) {
|
||||||
//Decrease the reference count for map
|
val removed = map.remove(el.key, value)
|
||||||
value.content.release()
|
if (removed) {
|
||||||
|
updateSizeAfterRemoval(value.content)
|
||||||
|
//Decrease the reference count for map
|
||||||
|
value.content.release()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
removalQueue.put(el)
|
||||||
|
Thread.sleep(minOf(Duration.between(now, el.expiry), Duration.ofSeconds(1)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
complete(null)
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
completeExceptionally(ex)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
removalQueue.put(el)
|
|
||||||
Thread.sleep(minOf(Duration.between(now, el.expiry), Duration.ofSeconds(1)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeEldest(): Long {
|
fun removeEldest(): Long {
|
||||||
while (true) {
|
while (true) {
|
||||||
val el = removalQueue.take()
|
val el = removalQueue.take()
|
||||||
val value = el.value
|
val value = el.value
|
||||||
@@ -84,9 +95,9 @@ class InMemoryCache(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun asyncClose() : CompletableFuture<Void> {
|
||||||
running = false
|
running = false
|
||||||
garbageCollector.join()
|
return closeFuture
|
||||||
}
|
}
|
||||||
|
|
||||||
fun get(key: ByteArray) = map[CacheKey(key)]?.run {
|
fun get(key: ByteArray) = map[CacheKey(key)]?.run {
|
||||||
|
@@ -1,5 +1,10 @@
|
|||||||
package net.woggioni.rbcs.server.cache
|
package net.woggioni.rbcs.server.cache
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelFactory
|
||||||
|
import io.netty.channel.EventLoopGroup
|
||||||
|
import io.netty.channel.socket.DatagramChannel
|
||||||
|
import io.netty.channel.socket.SocketChannel
|
||||||
|
import io.netty.util.concurrent.Future
|
||||||
import net.woggioni.rbcs.api.CacheHandlerFactory
|
import net.woggioni.rbcs.api.CacheHandlerFactory
|
||||||
import net.woggioni.rbcs.api.Configuration
|
import net.woggioni.rbcs.api.Configuration
|
||||||
import net.woggioni.rbcs.common.RBCS
|
import net.woggioni.rbcs.common.RBCS
|
||||||
@@ -16,11 +21,13 @@ data class InMemoryCacheConfiguration(
|
|||||||
override fun materialize() = object : CacheHandlerFactory {
|
override fun materialize() = object : CacheHandlerFactory {
|
||||||
private val cache = InMemoryCache(maxAge, maxSize)
|
private val cache = InMemoryCache(maxAge, maxSize)
|
||||||
|
|
||||||
override fun close() {
|
override fun asyncClose() = cache.asyncClose()
|
||||||
cache.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun newHandler() = InMemoryCacheHandler(cache, digestAlgorithm, compressionEnabled, compressionLevel)
|
override fun newHandler(
|
||||||
|
eventLoop: EventLoopGroup,
|
||||||
|
socketChannelFactory: ChannelFactory<SocketChannel>,
|
||||||
|
datagramChannelFactory: ChannelFactory<DatagramChannel>
|
||||||
|
) = InMemoryCacheHandler(cache, digestAlgorithm, compressionEnabled, compressionLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNamespaceURI() = RBCS.RBCS_NAMESPACE_URI
|
override fun getNamespaceURI() = RBCS.RBCS_NAMESPACE_URI
|
||||||
|
@@ -30,7 +30,7 @@ class InMemoryCacheProvider : CacheProvider<InMemoryCacheConfiguration> {
|
|||||||
val compressionLevel = el.renderAttribute("compression-level")
|
val compressionLevel = el.renderAttribute("compression-level")
|
||||||
?.let(String::toInt)
|
?.let(String::toInt)
|
||||||
?: Deflater.DEFAULT_COMPRESSION
|
?: Deflater.DEFAULT_COMPRESSION
|
||||||
val digestAlgorithm = el.renderAttribute("digest") ?: "MD5"
|
val digestAlgorithm = el.renderAttribute("digest")
|
||||||
val chunkSize = el.renderAttribute("chunk-size")
|
val chunkSize = el.renderAttribute("chunk-size")
|
||||||
?.let(Integer::decode)
|
?.let(Integer::decode)
|
||||||
?: 0x10000
|
?: 0x10000
|
||||||
|
@@ -153,7 +153,7 @@
|
|||||||
</xs:documentation>
|
</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="digest" type="xs:token" default="MD5">
|
<xs:attribute name="digest" type="xs:token">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>
|
<xs:documentation>
|
||||||
Hashing algorithm to apply to the key. If omitted, no hashing is performed.
|
Hashing algorithm to apply to the key. If omitted, no hashing is performed.
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
</xs:documentation>
|
</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="digest" type="xs:token" default="MD5">
|
<xs:attribute name="digest" type="xs:token" default="SHA3-224">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>
|
<xs:documentation>
|
||||||
Hashing algorithm to apply to the key. If omitted, no hashing is performed.
|
Hashing algorithm to apply to the key. If omitted, no hashing is performed.
|
||||||
|
Reference in New Issue
Block a user