Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
17215b401a
|
|||
4aced1c717
|
|||
31ce34cddb
|
|||
d64f7f4f27
|
|||
d15235fc4c
|
|||
49bb4f41b8
|
148
README.md
148
README.md
@@ -1,4 +1,45 @@
|
|||||||
# Remote Build Cache Server
|
# Remote Build Cache Server
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
<!--
|
||||||
|

|
||||||
|
-->
|
||||||
|
|
||||||
|
Speed up your builds by sharing and reusing unchanged build outputs across your team.
|
||||||
|
|
||||||
|
Remote Build Cache Server (RBCS) allows teams to share and reuse unchanged build and test outputs,
|
||||||
|
significantly reducing build times for both local and CI environments. By eliminating redundant work,
|
||||||
|
RBCS helps teams become more productive and efficient.
|
||||||
|
|
||||||
|
**Key Features:**
|
||||||
|
- Support for both Gradle and Maven build environments
|
||||||
|
- Pluggable storage backends (in-memory, disk-backed, memcached)
|
||||||
|
- Flexible authentication (HTTP basic or TLS certificate)
|
||||||
|
- Role-based access control
|
||||||
|
- Request throttling
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
- [Quickstart](#quickstart)
|
||||||
|
- [Integration with build tools](#integration-with-build-tools)
|
||||||
|
- [Use RBCS with Gradle](#use-rbcs-with-gradle)
|
||||||
|
- [Use RBCS with Maven](#use-rbcs-with-maven)
|
||||||
|
- [Server configuration](#server-configuration)
|
||||||
|
- [Authentication](#authentication)
|
||||||
|
- [HTTP Basic authentication](#configure-http-basic-authentication)
|
||||||
|
- [TLS client certificate authentication](#configure-tls-certificate-authentication)
|
||||||
|
- [Authentication & Access Control](#access-control)
|
||||||
|
- [Plugins](#plugins)
|
||||||
|
- [Client Tools](#rbcs-client)
|
||||||
|
- [Logging](#logging)
|
||||||
|
- [Performance](#performance)
|
||||||
|
- [FAQ](#faq)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Remote Build Cache Server (shortened to RBCS) allows you to share and reuse unchanged build
|
Remote Build Cache Server (shortened to RBCS) allows you to share and reuse unchanged build
|
||||||
and test outputs across the team. This speeds up local and CI builds since cycles are not wasted
|
and test outputs across the team. This speeds up local and CI builds since cycles are not wasted
|
||||||
re-building components that are unaffected by new code changes. RBCS supports both Gradle and
|
re-building components that are unaffected by new code changes. RBCS supports both Gradle and
|
||||||
@@ -12,7 +53,7 @@ and throttling.
|
|||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
### Downloading the jar file
|
### Use the all-in-one jar file
|
||||||
You can download the latest version from [this link](https://gitea.woggioni.net/woggioni/-/packages/maven/net.woggioni:rbcs-cli/)
|
You can download the latest version from [this link](https://gitea.woggioni.net/woggioni/-/packages/maven/net.woggioni:rbcs-cli/)
|
||||||
|
|
||||||
|
|
||||||
@@ -25,7 +66,7 @@ java -jar rbcs-cli.jar server
|
|||||||
By default it will start an HTTP server bound to localhost and listening on port 8080 with no authentication,
|
By default it will start an HTTP server bound to localhost and listening on port 8080 with no authentication,
|
||||||
writing data to the disk, that you can use for testing
|
writing data to the disk, that you can use for testing
|
||||||
|
|
||||||
### Using the Docker image
|
### Use the Docker image
|
||||||
You can pull the latest Docker image with
|
You can pull the latest Docker image with
|
||||||
```bash
|
```bash
|
||||||
docker pull gitea.woggioni.net/woggioni/rbcs:latest
|
docker pull gitea.woggioni.net/woggioni/rbcs:latest
|
||||||
@@ -34,41 +75,16 @@ docker pull gitea.woggioni.net/woggioni/rbcs:latest
|
|||||||
By default it will start an HTTP server bound to localhost and listening on port 8080 with no authentication,
|
By default it will start an HTTP server bound to localhost and listening on port 8080 with no authentication,
|
||||||
writing data to the disk, that you can use for testing
|
writing data to the disk, that you can use for testing
|
||||||
|
|
||||||
### Using the native executable
|
### Use the native executable
|
||||||
If you are on a Linux X86_64 machine you can download the native executable
|
If you are on a Linux X86_64 machine you can download the native executable
|
||||||
from [here](https://gitea.woggioni.net/woggioni/-/packages/maven/net.woggioni:rbcs-cli/).
|
from [here](https://gitea.woggioni.net/woggioni/-/packages/maven/net.woggioni:rbcs-cli/).
|
||||||
It behaves the same as the jar file but it doesn't require a JVM and it has faster startup times.
|
It behaves the same as the jar file but it doesn't require a JVM and it has faster startup times.
|
||||||
becausue of GraalVm's [closed-world assumption](https://www.graalvm.org/latest/reference-manual/native-image/basics/#static-analysis),
|
because of GraalVM's [closed-world assumption](https://www.graalvm.org/latest/reference-manual/native-image/basics/#static-analysis),
|
||||||
the native executable does not supports plugins, so it comes with all plugins embedded into it.
|
the native executable does not supports plugins, so it comes with all plugins embedded into it.
|
||||||
|
|
||||||
## Usage
|
## Integration with build tools
|
||||||
|
|
||||||
### Configuration
|
### Use RBCS with Gradle
|
||||||
The location of the `rbcs-server.xml` configuration file depends on the operating system,
|
|
||||||
Alternatively it can be changed setting the `RBCS_CONFIGURATION_DIR` environmental variable or `net.woggioni.rbcs.conf.dir` Java system property
|
|
||||||
to the directory that contain the `rbcs-server.xml` file.
|
|
||||||
|
|
||||||
The server configuration file follows the XML format and uses XML schema for validation
|
|
||||||
(you can find the schema for the main configuration file [here](https://gitea.woggioni.net/woggioni/rbcs/src/branch/master/rbcs-server/src/main/resources/net/woggioni/rbcs/server/schema/rbcs-server.xsd)).
|
|
||||||
|
|
||||||
The configuration values are enclosed inside XML attribute and support system property / environmental variable interpolation.
|
|
||||||
As an example, you can configure RBCS to read the server port number from the `RBCS_SERVER_PORT` environmental variable
|
|
||||||
and the bind address from the `rbc.bind.address` JVM system property with
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<bind host="${sys:rpc.bind.address}" port="${env:RBCS_SERVER_PORT}"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
Full documentation for all tags and attributes is available [here](doc/server_configuration.md).
|
|
||||||
|
|
||||||
### Plugins
|
|
||||||
If you want to use memcache as a storage backend you'll also need to download [the memcache plugin](https://gitea.woggioni.net/woggioni/-/packages/maven/net.woggioni:rbcs-server-memcache/)
|
|
||||||
|
|
||||||
Plugins need to be stored in a folder named `plugins` in the located server's working directory
|
|
||||||
(the directory where the server process is started). They are shipped as TAR archives, so you need to extract
|
|
||||||
the content of the archive into the `plugins` directory for the server to pick them up.
|
|
||||||
|
|
||||||
### Using RBCS with Gradle
|
|
||||||
|
|
||||||
Add this to the `settings.gradle` file of your project
|
Add this to the `settings.gradle` file of your project
|
||||||
|
|
||||||
@@ -113,7 +129,7 @@ add `org.gradle.caching=true` to your `<project>/gradle.properties` or run gradl
|
|||||||
|
|
||||||
Read [Gradle documentation](https://docs.gradle.org/current/userguide/build_cache.html) for more detailed information.
|
Read [Gradle documentation](https://docs.gradle.org/current/userguide/build_cache.html) for more detailed information.
|
||||||
|
|
||||||
### Using RBCS with Maven
|
### Use RBCS with Maven
|
||||||
|
|
||||||
1. Create an `extensions.xml` in `<project>/.mvn/extensions.xml` with the following content
|
1. Create an `extensions.xml` in `<project>/.mvn/extensions.xml` with the following content
|
||||||
```xml
|
```xml
|
||||||
@@ -143,6 +159,46 @@ 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
|
for more informations
|
||||||
|
|
||||||
|
|
||||||
|
## Server configuration
|
||||||
|
RBCS reads an XML configuration file, by default named `rbcs-server.xml`.
|
||||||
|
The expected location of the `rbcs-server.xml` file depends on the operating system,
|
||||||
|
if the configuration file is not found a default one will be created and its location is printed
|
||||||
|
on the console
|
||||||
|
|
||||||
|
```bash
|
||||||
|
user@76a90cbcd75d:~$ rbcs-cli server
|
||||||
|
2025-01-01 00:00:00,000 [INFO ] (main) n.w.r.c.impl.commands.ServerCommand -- Creating default configuration file at '/home/user/.config/rbcs/rbcs-server.xml'
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively it can be changed setting the `RBCS_CONFIGURATION_DIR` environmental variable or `net.woggioni.rbcs.conf.dir`
|
||||||
|
Java system property to the directory that contain the `rbcs-server.xml` file.
|
||||||
|
It can also be directly specified from the command line with
|
||||||
|
```bash
|
||||||
|
java -jar rbcs-cli.jar server -c /path/to/rbcs-server.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
The server configuration file follows the XML format and uses XML schema for validation
|
||||||
|
(you can find the schema for the `rbcs-server.xml` configuration file [here](https://gitea.woggioni.net/woggioni/rbcs/src/branch/master/rbcs-server/src/main/resources/net/woggioni/rbcs/server/schema/rbcs-server.xsd)).
|
||||||
|
|
||||||
|
The configuration values are enclosed inside XML attribute and support system property / environmental variable interpolation.
|
||||||
|
As an example, you can configure RBCS to read the server port number from the `RBCS_SERVER_PORT` environmental variable
|
||||||
|
and the bind address from the `rbc.bind.address` JVM system property with
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<bind host="${sys:rpc.bind.address}" port="${env:RBCS_SERVER_PORT}"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
Full documentation for all tags and attributes and configuration file examples
|
||||||
|
are available [here](doc/server_configuration.md).
|
||||||
|
|
||||||
|
### Plugins
|
||||||
|
If you want to use memcache as a storage backend you'll also need to download [the memcache plugin](https://gitea.woggioni.net/woggioni/-/packages/maven/net.woggioni:rbcs-server-memcache/)
|
||||||
|
|
||||||
|
Plugins need to be stored in a folder named `plugins` in the located server's working directory
|
||||||
|
(the directory where the server process is started). They are shipped as TAR archives, so you need to extract
|
||||||
|
the content of the archive into the `plugins` directory for the server to pick them up.
|
||||||
|
|
||||||
## Authentication
|
## Authentication
|
||||||
|
|
||||||
RBCS supports 2 authentication mechanisms:
|
RBCS supports 2 authentication mechanisms:
|
||||||
@@ -250,7 +306,11 @@ as a health check (mind you need to have `Healthcheck` role in order to perform
|
|||||||
|
|
||||||
RBCS ships with a command line client that can be used for testing, benchmarking or to manually
|
RBCS ships with a command line client that can be used for testing, benchmarking or to manually
|
||||||
upload/download files to the cache. It must be configured with the `rbcs-client.xml`,
|
upload/download files to the cache. It must be configured with the `rbcs-client.xml`,
|
||||||
whose location follows the same logic of the `rbcs-server.xml`
|
whose location follows the same logic of the `rbcs-server.xml`.
|
||||||
|
The `rbcs-client.xml` must adhere to the [rbcs-client.xsd](rbcs-client/src/main/resources/net/woggioni/rbcs/client/schema/rbcs-client.xsd)
|
||||||
|
XML schema
|
||||||
|
|
||||||
|
The documentation for the `rbcs-client.xml` configuration file is available [here](conf/client_configuration.md)
|
||||||
|
|
||||||
### GET command
|
### GET command
|
||||||
|
|
||||||
@@ -263,6 +323,24 @@ java -jar rbcs-cli.jar client -p $CLIENT_PROFILE_NAME get -k $CACHE_KEY -v $FILE
|
|||||||
```bash
|
```bash
|
||||||
java -jar rbcs-cli.jar client -p $CLIENT_PROFILE_NAME put -k $CACHE_KEY -v $FILE_TO_BE_UPLOADED
|
java -jar rbcs-cli.jar client -p $CLIENT_PROFILE_NAME put -k $CACHE_KEY -v $FILE_TO_BE_UPLOADED
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you don't specify the key, a UUID key based on the file content will be used,
|
||||||
|
if you add the `-i` command line parameter, the uploaded file will be served with
|
||||||
|
`Content-Disposition: inline` HTTP header so that browser will attempt to render
|
||||||
|
it in the page instead of triggering a file download (in this way you can create a temporary web page).
|
||||||
|
|
||||||
|
The client will try to detect the file mime type upon upload but if you want to be sure you can specify
|
||||||
|
it manually with the `-t` parameter.
|
||||||
|
|
||||||
|
### Benchmark command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
java -jar rbcs-cli.jar client -p $CLIENT_PROFILE_NAME benchamrk -s 4096 -e 10000
|
||||||
|
```
|
||||||
|
This will insert 10000 randomly generates entries of 4096 bytes into RBCS, then retrieve them
|
||||||
|
and check that the retrieved value matches what was inserted.
|
||||||
|
It will also print throughput stats on the way.
|
||||||
|
|
||||||
## Logging
|
## Logging
|
||||||
|
|
||||||
RBCS uses [logback](https://logback.qos.ch/) and ships with a [default logging configuration](./conf/logback.xml) that
|
RBCS uses [logback](https://logback.qos.ch/) and ships with a [default logging configuration](./conf/logback.xml) that
|
||||||
@@ -270,6 +348,10 @@ can be overridden with `-Dlogback.configurationFile=path/to/custom/configuration
|
|||||||
[Logback documentation](https://logback.qos.ch/manual/configuration.html) for more details about
|
[Logback documentation](https://logback.qos.ch/manual/configuration.html) for more details about
|
||||||
how to configure Logback
|
how to configure Logback
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
You can check performance benchmarks [here](doc/benchmarks.md)
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
### Why should I use a build cache?
|
### Why should I use a build cache?
|
||||||
|
|
||||||
|
@@ -38,8 +38,8 @@ allprojects { subproject ->
|
|||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
modularity.inferModulePath = true
|
modularity.inferModulePath = true
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion = JavaLanguageVersion.of(23)
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
vendor = JvmVendorSpec.ORACLE
|
vendor = JvmVendorSpec.GRAAL_VM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
87
doc/benchmarks.md
Normal file
87
doc/benchmarks.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
# RBCS performance benchmarks
|
||||||
|
|
||||||
|
All test were executed under the following conditions:
|
||||||
|
- CPU: Intel Celeron J3455 (4 physical cores)
|
||||||
|
- memory: 8GB DDR3L 1600 MHz
|
||||||
|
- disk: SATA3 120GB SSD
|
||||||
|
- HTTP compression: disabled
|
||||||
|
- cache compression: disabled
|
||||||
|
- digest: none
|
||||||
|
- authentication: disabled
|
||||||
|
- TLS: disabled
|
||||||
|
- network RTT: 14ms
|
||||||
|
- network bandwidth: 112 MiB/s
|
||||||
|
### In memory cache backend
|
||||||
|
|
||||||
|
|
||||||
|
| Cache backend | CPU | CPU quota | Memory quota (GB) | Request size (b) | Client connections | PUT (req/s) | GET (req/s) |
|
||||||
|
|----------------|---------------------|-----------|-------------------|------------------|--------------------|-------------|-------------|
|
||||||
|
| in-memory | Intel Celeron J3455 | 1.00 | 4 | 128 | 10 | 3691 | 4037 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 1.00 | 4 | 128 | 100 | 6881 | 7483 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 1.00 | 4 | 512 | 10 | 3790 | 4069 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 1.00 | 4 | 512 | 100 | 6716 | 7408 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 1.00 | 4 | 4096 | 10 | 3399 | 1974 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 1.00 | 4 | 4096 | 100 | 5341 | 6402 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 1.00 | 4 | 65536 | 10 | 1099 | 1116 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 1.00 | 4 | 65536 | 100 | 1379 | 1703 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 3.50 | 4 | 128 | 10 | 4443 | 5170 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 3.50 | 4 | 128 | 100 | 12813 | 13568 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 3.50 | 4 | 512 | 10 | 4450 | 4383 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 3.50 | 4 | 512 | 100 | 12212 | 13586 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 3.50 | 4 | 4096 | 10 | 3441 | 3012 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 3.50 | 4 | 4096 | 100 | 8982 | 10452 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 3.50 | 4 | 65536 | 10 | 1391 | 1167 |
|
||||||
|
| in-memory | Intel Celeron J3455 | 3.50 | 4 | 65536 | 100 | 1303 | 1151 |
|
||||||
|
|
||||||
|
### Filesystem cache backend
|
||||||
|
|
||||||
|
compression: disabled
|
||||||
|
digest: none
|
||||||
|
authentication: disabled
|
||||||
|
TLS: disabled
|
||||||
|
|
||||||
|
| Cache backend | CPU | CPU quota | Memory quota (GB) | Request size (b) | Client connections | PUT (req/s) | GET (req/s) |
|
||||||
|
|---------------|---------------------|-----------|-------------------|------------------|--------------------|-------------|-------------|
|
||||||
|
| filesystem | Intel Celeron J3455 | 1.00 | 0.25 | 128 | 10 | 1208 | 2048 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 1.00 | 0.25 | 128 | 100 | 1304 | 2394 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 1.00 | 0.25 | 512 | 10 | 1408 | 2157 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 1.00 | 0.25 | 512 | 100 | 1282 | 1888 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 1.00 | 0.25 | 4096 | 10 | 1291 | 1256 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 1.00 | 0.25 | 4096 | 100 | 1170 | 1423 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 1.00 | 0.25 | 65536 | 10 | 313 | 606 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 1.00 | 0.25 | 65536 | 100 | 298 | 609 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 3.50 | 0.25 | 128 | 10 | 2195 | 3477 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 3.50 | 0.25 | 128 | 100 | 2480 | 6207 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 3.50 | 0.25 | 512 | 10 | 2164 | 3413 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 3.50 | 0.25 | 512 | 100 | 2842 | 6218 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 3.50 | 0.25 | 4096 | 10 | 1302 | 2591 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 3.50 | 0.25 | 4096 | 100 | 2270 | 3045 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 3.50 | 0.25 | 65536 | 10 | 375 | 394 |
|
||||||
|
| filesystem | Intel Celeron J3455 | 3.50 | 0.25 | 65536 | 100 | 364 | 462 |
|
||||||
|
|
||||||
|
|
||||||
|
### Memcache cache backend
|
||||||
|
|
||||||
|
compression: disabled
|
||||||
|
digest: MD5
|
||||||
|
authentication: disabled
|
||||||
|
TLS: disabled
|
||||||
|
|
||||||
|
| Cache backend | CPU | CPU quota | Memory quota (GB) | Request size (b) | Client connections | PUT (req/s) | GET (req/s) |
|
||||||
|
|---------------|---------------------|-----------|-------------------|------------------|--------------------|-------------|-------------|
|
||||||
|
| memcache | Intel Celeron J3455 | 1.00 | 0.25 | 128 | 10 | 2505 | 2578 |
|
||||||
|
| memcache | Intel Celeron J3455 | 1.00 | 0.25 | 128 | 100 | 3582 | 3935 |
|
||||||
|
| memcache | Intel Celeron J3455 | 1.00 | 0.25 | 512 | 10 | 2495 | 2784 |
|
||||||
|
| memcache | Intel Celeron J3455 | 1.00 | 0.25 | 512 | 100 | 3565 | 3883 |
|
||||||
|
| memcache | Intel Celeron J3455 | 1.00 | 0.25 | 4096 | 10 | 2174 | 2505 |
|
||||||
|
| memcache | Intel Celeron J3455 | 1.00 | 0.25 | 4096 | 100 | 2937 | 3563 |
|
||||||
|
| memcache | Intel Celeron J3455 | 1.00 | 0.25 | 65536 | 10 | 648 | 1074 |
|
||||||
|
| memcache | Intel Celeron J3455 | 1.00 | 0.25 | 65536 | 100 | 724 | 1548 |
|
||||||
|
| memcache | Intel Celeron J3455 | 3.50 | 0.25 | 128 | 10 | 2362 | 2927 |
|
||||||
|
| memcache | Intel Celeron J3455 | 3.50 | 0.25 | 128 | 100 | 5491 | 6531 |
|
||||||
|
| memcache | Intel Celeron J3455 | 3.50 | 0.25 | 512 | 10 | 2125 | 2807 |
|
||||||
|
| memcache | Intel Celeron J3455 | 3.50 | 0.25 | 512 | 100 | 5173 | 6242 |
|
||||||
|
| memcache | Intel Celeron J3455 | 3.50 | 0.25 | 4096 | 10 | 1720 | 2397 |
|
||||||
|
| memcache | Intel Celeron J3455 | 3.50 | 0.25 | 4096 | 100 | 3871 | 5859 |
|
||||||
|
| memcache | Intel Celeron J3455 | 3.50 | 0.25 | 65536 | 10 | 616 | 1016 |
|
||||||
|
| memcache | Intel Celeron J3455 | 3.50 | 0.25 | 65536 | 100 | 820 | 1677 |
|
@@ -137,9 +137,22 @@ Configures TLS encryption.
|
|||||||
read-timeout="PT5S"
|
read-timeout="PT5S"
|
||||||
write-timeout="PT5S"/>
|
write-timeout="PT5S"/>
|
||||||
<event-executor use-virtual-threads="true"/>
|
<event-executor use-virtual-threads="true"/>
|
||||||
|
|
||||||
<cache xs:type="rbcs:inMemoryCacheType" max-age="P7D" enable-compression="false" max-size="0x10000000" />
|
<cache xs:type="rbcs:inMemoryCacheType" max-age="P7D" enable-compression="false" max-size="0x10000000" />
|
||||||
<!--cache xs:type="rbcs:fileSystemCacheType" max-age="P7D" enable-compression="false" path="${sys:java.io.tmpdir}/rbcs"/-->
|
|
||||||
<authorization>
|
<!-- uncomment this to enable the filesystem storage backend, sotring cache data in "${sys:java.io.tmpdir}/rbcs"
|
||||||
|
<cache xs:type="rbcs:fileSystemCacheType" max-age="P7D" enable-compression="false" path="${sys:java.io.tmpdir}/rbcs"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- uncomment this to use memcache as the storage backend, also make sure you have
|
||||||
|
the memcache plugin installed in the `plugins` directory if you are using running
|
||||||
|
the jar version of RBCS
|
||||||
|
<cache xs:type="rbcs-memcache:memcacheCacheType" max-age="P7D" chunk-size="0x1000" digest="MD5">
|
||||||
|
<server host="127.0.0.1" port="11211" max-connections="256"/>
|
||||||
|
</cache>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<authorization>
|
||||||
<users>
|
<users>
|
||||||
<user name="user1" password="II+qeNLft2pZ/JVNo9F7jpjM/BqEcfsJW27NZ6dPVs8tAwHbxrJppKYsbL7J/SMl">
|
<user name="user1" password="II+qeNLft2pZ/JVNo9F7jpjM/BqEcfsJW27NZ6dPVs8tAwHbxrJppKYsbL7J/SMl">
|
||||||
<quota calls="100" period="PT1S"/>
|
<quota calls="100" period="PT1S"/>
|
||||||
|
@@ -4,7 +4,7 @@ org.gradle.caching=true
|
|||||||
|
|
||||||
rbcs.version = 0.2.0
|
rbcs.version = 0.2.0
|
||||||
|
|
||||||
lys.version = 2025.02.26
|
lys.version = 2025.03.03
|
||||||
|
|
||||||
gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven
|
gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven
|
||||||
docker.registry.url=gitea.woggioni.net
|
docker.registry.url=gitea.woggioni.net
|
||||||
|
@@ -12,6 +12,7 @@ plugins {
|
|||||||
import net.woggioni.gradle.envelope.EnvelopePlugin
|
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.NativeImageTask
|
||||||
import net.woggioni.gradle.graalvm.NativeImagePlugin
|
import net.woggioni.gradle.graalvm.NativeImagePlugin
|
||||||
import net.woggioni.gradle.graalvm.UpxTask
|
import net.woggioni.gradle.graalvm.UpxTask
|
||||||
import net.woggioni.gradle.graalvm.JlinkPlugin
|
import net.woggioni.gradle.graalvm.JlinkPlugin
|
||||||
@@ -90,11 +91,6 @@ Provider<EnvelopeJarTask> envelopeJarTaskProvider = tasks.named(EnvelopePlugin.E
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks.named(NativeImagePlugin.CONFIGURE_NATIVE_IMAGE_TASK_NAME, NativeImageConfigurationTask) {
|
tasks.named(NativeImagePlugin.CONFIGURE_NATIVE_IMAGE_TASK_NAME, NativeImageConfigurationTask) {
|
||||||
javaLauncher = javaToolchains.launcherFor {
|
|
||||||
languageVersion = JavaLanguageVersion.of(21)
|
|
||||||
vendor = JvmVendorSpec.ORACLE
|
|
||||||
}
|
|
||||||
|
|
||||||
mainClass = "net.woggioni.rbcs.cli.graal.GraalNativeImageConfiguration"
|
mainClass = "net.woggioni.rbcs.cli.graal.GraalNativeImageConfiguration"
|
||||||
classpath = project.files(
|
classpath = project.files(
|
||||||
configurations.configureNativeImageRuntimeClasspath,
|
configurations.configureNativeImageRuntimeClasspath,
|
||||||
@@ -108,6 +104,10 @@ tasks.named(NativeImagePlugin.CONFIGURE_NATIVE_IMAGE_TASK_NAME, NativeImageConfi
|
|||||||
}
|
}
|
||||||
|
|
||||||
nativeImage {
|
nativeImage {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(23)
|
||||||
|
vendor = JvmVendorSpec.GRAAL_VM
|
||||||
|
}
|
||||||
mainClass = mainClassName
|
mainClass = mainClassName
|
||||||
// mainModule = mainModuleName
|
// mainModule = mainModuleName
|
||||||
useMusl = true
|
useMusl = true
|
||||||
|
53
rbcs-cli/conf/rbcs-server.xml
Normal file
53
rbcs-cli/conf/rbcs-server.xml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:rbcs="urn:net.woggioni.rbcs.server"
|
||||||
|
xmlns:rbcs-memcache="urn:net.woggioni.rbcs.server.memcache"
|
||||||
|
xs:schemaLocation="urn:net.woggioni.rbcs.server.memcache jpms://net.woggioni.rbcs.server.memcache/net/woggioni/rbcs/server/memcache/schema/rbcs-memcache.xsd urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd"
|
||||||
|
>
|
||||||
|
<bind host="127.0.0.1" port="8080" incoming-connections-backlog-size="1024"/>
|
||||||
|
<connection
|
||||||
|
max-request-size="67108864"
|
||||||
|
idle-timeout="PT10S"
|
||||||
|
read-idle-timeout="PT20S"
|
||||||
|
write-idle-timeout="PT20S"/>
|
||||||
|
<event-executor use-virtual-threads="true"/>
|
||||||
|
<cache xs:type="rbcs-memcache:memcacheCacheType" max-age="P7D" chunk-size="0x1000" digest="MD5">
|
||||||
|
<server host="127.0.0.1" port="11211" max-connections="256"/>
|
||||||
|
</cache>
|
||||||
|
<!--cache xs:type="rbcs:inMemoryCacheType" max-age="P7D" enable-compression="false" max-size="0x10000000" /-->
|
||||||
|
<!--cache xs:type="rbcs:fileSystemCacheType" max-age="P7D" enable-compression="false" /-->
|
||||||
|
<authorization>
|
||||||
|
<users>
|
||||||
|
<user name="woggioni" password="II+qeNLft2pZ/JVNo9F7jpjM/BqEcfsJW27NZ6dPVs8tAwHbxrJppKYsbL7J/SMl">
|
||||||
|
<quota calls="100" period="PT1S"/>
|
||||||
|
</user>
|
||||||
|
<user name="gitea" password="v6T9+q6/VNpvLknji3ixPiyz2YZCQMXj2FN7hvzbfc2Ig+IzAHO0iiBCH9oWuBDq"/>
|
||||||
|
<anonymous>
|
||||||
|
<quota calls="10" period="PT60S" initial-available-calls="10" max-available-calls="10"/>
|
||||||
|
</anonymous>
|
||||||
|
</users>
|
||||||
|
<groups>
|
||||||
|
<group name="readers">
|
||||||
|
<users>
|
||||||
|
<anonymous/>
|
||||||
|
</users>
|
||||||
|
<roles>
|
||||||
|
<reader/>
|
||||||
|
</roles>
|
||||||
|
</group>
|
||||||
|
<group name="writers">
|
||||||
|
<users>
|
||||||
|
<user ref="woggioni"/>
|
||||||
|
<user ref="gitea"/>
|
||||||
|
</users>
|
||||||
|
<roles>
|
||||||
|
<reader/>
|
||||||
|
<writer/>
|
||||||
|
</roles>
|
||||||
|
</group>
|
||||||
|
</groups>
|
||||||
|
</authorization>
|
||||||
|
<authentication>
|
||||||
|
<none/>
|
||||||
|
</authentication>
|
||||||
|
</rbcs:server>
|
@@ -1,2 +1,2 @@
|
|||||||
Args=-O3 --gc=serial --install-exit-handlers --initialize-at-run-time=io.netty --enable-url-protocols=jpms --initialize-at-build-time=net.woggioni.rbcs.common.RbcsUrlStreamHandlerFactory,net.woggioni.rbcs.common.RbcsUrlStreamHandlerFactory$JpmsHandler
|
Args=-O3 -march=skylake --gc=serial --install-exit-handlers --initialize-at-run-time=io.netty --enable-url-protocols=jpms --initialize-at-build-time=net.woggioni.rbcs.common.RbcsUrlStreamHandlerFactory,net.woggioni.rbcs.common.RbcsUrlStreamHandlerFactory$JpmsHandler
|
||||||
#-H:TraceClassInitialization=io.netty.handler.ssl.BouncyCastleAlpnSslUtils
|
#-H:TraceClassInitialization=io.netty.handler.ssl.BouncyCastleAlpnSslUtils
|
@@ -36,6 +36,8 @@
|
|||||||
"pattern":"\\Qnet/woggioni/rbcs/server/rbcs-default.xml\\E"
|
"pattern":"\\Qnet/woggioni/rbcs/server/rbcs-default.xml\\E"
|
||||||
}, {
|
}, {
|
||||||
"pattern":"\\Qnet/woggioni/rbcs/server/schema/rbcs-server.xsd\\E"
|
"pattern":"\\Qnet/woggioni/rbcs/server/schema/rbcs-server.xsd\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\Q/net/woggioni/rbcs/server/memcache/schema/rbcs-memcache.xsd\\E"
|
||||||
}, {
|
}, {
|
||||||
"pattern":"java.base:\\Qsun/text/resources/LineBreakIteratorData\\E"
|
"pattern":"java.base:\\Qsun/text/resources/LineBreakIteratorData\\E"
|
||||||
}]},
|
}]},
|
||||||
|
@@ -32,8 +32,9 @@ object GraalNativeImageConfiguration {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun main(vararg args : String) {
|
fun main(vararg args : String) {
|
||||||
|
|
||||||
val serverDoc = RemoteBuildCacheServer.DEFAULT_CONFIGURATION_URL.openStream().use {
|
val serverURL = URI.create("file:conf/rbcs-client.xml").toURL()
|
||||||
Xml.parseXml(RemoteBuildCacheServer.DEFAULT_CONFIGURATION_URL, it)
|
val serverDoc = serverURL.openStream().use {
|
||||||
|
Xml.parseXml(serverURL, it)
|
||||||
}
|
}
|
||||||
Parser.parse(serverDoc)
|
Parser.parse(serverDoc)
|
||||||
|
|
||||||
|
@@ -9,6 +9,7 @@ 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.common.createLogger
|
||||||
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.CompletableFuture
|
||||||
@@ -25,6 +26,10 @@ data class MemcacheCacheConfiguration(
|
|||||||
val chunkSize: Int
|
val chunkSize: Int
|
||||||
) : Configuration.Cache {
|
) : Configuration.Cache {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val log = createLogger<MemcacheCacheConfiguration>()
|
||||||
|
}
|
||||||
|
|
||||||
enum class CompressionMode {
|
enum class CompressionMode {
|
||||||
/**
|
/**
|
||||||
* Deflate mode
|
* Deflate mode
|
||||||
@@ -69,15 +74,19 @@ data class MemcacheCacheConfiguration(
|
|||||||
val pools = connectionPoolMap.values.toList()
|
val pools = connectionPoolMap.values.toList()
|
||||||
val npools = pools.size
|
val npools = pools.size
|
||||||
val finished = AtomicInteger(0)
|
val finished = AtomicInteger(0)
|
||||||
pools.forEach { pool ->
|
if (pools.isEmpty()) {
|
||||||
pool.closeAsync().addListener {
|
complete(null)
|
||||||
if (!it.isSuccess) {
|
} else {
|
||||||
failure.compareAndSet(null, it.cause())
|
pools.forEach { pool ->
|
||||||
}
|
pool.closeAsync().addListener {
|
||||||
if(finished.incrementAndGet() == npools) {
|
if (!it.isSuccess) {
|
||||||
when(val ex = failure.get()) {
|
failure.compareAndSet(null, it.cause())
|
||||||
null -> complete(null)
|
}
|
||||||
else -> completeExceptionally(ex)
|
if (finished.incrementAndGet() == npools) {
|
||||||
|
when (val ex = failure.get()) {
|
||||||
|
null -> complete(null)
|
||||||
|
else -> completeExceptionally(ex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -368,13 +368,14 @@ class RemoteBuildCacheServer(private val cfg: Configuration) {
|
|||||||
private val bossGroup: EventExecutorGroup,
|
private val bossGroup: EventExecutorGroup,
|
||||||
private val executorGroups: Iterable<EventExecutorGroup>,
|
private val executorGroups: Iterable<EventExecutorGroup>,
|
||||||
private val serverInitializer: AsyncCloseable,
|
private val serverInitializer: AsyncCloseable,
|
||||||
) : Future<Void> by from(closeFuture, executorGroups, serverInitializer) {
|
) : Future<Void> by from(closeFuture, bossGroup, executorGroups, serverInitializer) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = createLogger<ServerHandle>()
|
private val log = createLogger<ServerHandle>()
|
||||||
|
|
||||||
private fun from(
|
private fun from(
|
||||||
closeFuture: ChannelFuture,
|
closeFuture: ChannelFuture,
|
||||||
|
bossGroup: EventExecutorGroup,
|
||||||
executorGroups: Iterable<EventExecutorGroup>,
|
executorGroups: Iterable<EventExecutorGroup>,
|
||||||
serverInitializer: AsyncCloseable
|
serverInitializer: AsyncCloseable
|
||||||
): CompletableFuture<Void> {
|
): CompletableFuture<Void> {
|
||||||
@@ -382,22 +383,15 @@ class RemoteBuildCacheServer(private val cfg: Configuration) {
|
|||||||
closeFuture.addListener {
|
closeFuture.addListener {
|
||||||
val errors = mutableListOf<Throwable>()
|
val errors = mutableListOf<Throwable>()
|
||||||
val deadline = Instant.now().plusSeconds(20)
|
val deadline = Instant.now().plusSeconds(20)
|
||||||
try {
|
|
||||||
serverInitializer.close()
|
|
||||||
} catch (ex: Throwable) {
|
|
||||||
log.error(ex.message, ex)
|
|
||||||
errors.addLast(ex)
|
|
||||||
}
|
|
||||||
|
|
||||||
serverInitializer.asyncClose().whenComplete { _, ex ->
|
serverInitializer.asyncClose().whenCompleteAsync { _, ex ->
|
||||||
if(ex != null) {
|
if(ex != null) {
|
||||||
log.error(ex.message, ex)
|
log.error(ex.message, ex)
|
||||||
errors.addLast(ex)
|
errors.addLast(ex)
|
||||||
}
|
}
|
||||||
|
|
||||||
executorGroups.map {
|
executorGroups.forEach(EventExecutorGroup::shutdownGracefully)
|
||||||
it.shutdownGracefully()
|
bossGroup.terminationFuture().sync()
|
||||||
}
|
|
||||||
|
|
||||||
for (executorGroup in executorGroups) {
|
for (executorGroup in executorGroups) {
|
||||||
val future = executorGroup.terminationFuture()
|
val future = executorGroup.terminationFuture()
|
||||||
|
@@ -6,11 +6,11 @@ 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.PriorityQueue
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
import java.util.concurrent.PriorityBlockingQueue
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
private class CacheKey(private val value: ByteArray) {
|
private class CacheKey(private val value: ByteArray) {
|
||||||
override fun equals(other: Any?) = if (other is CacheKey) {
|
override fun equals(other: Any?) = if (other is CacheKey) {
|
||||||
@@ -34,15 +34,17 @@ class InMemoryCache(
|
|||||||
private val log = createLogger<InMemoryCache>()
|
private val log = createLogger<InMemoryCache>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val size = AtomicLong()
|
private var mapSize : Long = 0
|
||||||
private val map = ConcurrentHashMap<CacheKey, CacheEntry>()
|
private val map = HashMap<CacheKey, CacheEntry>()
|
||||||
|
private val lock = ReentrantReadWriteLock()
|
||||||
|
private val cond = lock.writeLock().newCondition()
|
||||||
|
|
||||||
private class RemovalQueueElement(val key: CacheKey, val value: CacheEntry, val expiry: Instant) :
|
private class RemovalQueueElement(val key: CacheKey, val value: CacheEntry, val expiry: Instant) :
|
||||||
Comparable<RemovalQueueElement> {
|
Comparable<RemovalQueueElement> {
|
||||||
override fun compareTo(other: RemovalQueueElement) = expiry.compareTo(other.expiry)
|
override fun compareTo(other: RemovalQueueElement) = expiry.compareTo(other.expiry)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val removalQueue = PriorityBlockingQueue<RemovalQueueElement>()
|
private val removalQueue = PriorityQueue<RemovalQueueElement>()
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var running = true
|
private var running = true
|
||||||
@@ -51,20 +53,27 @@ class InMemoryCache(
|
|||||||
init {
|
init {
|
||||||
Thread.ofVirtual().name("in-memory-cache-gc").start {
|
Thread.ofVirtual().name("in-memory-cache-gc").start {
|
||||||
try {
|
try {
|
||||||
while (running) {
|
lock.writeLock().withLock {
|
||||||
val el = removalQueue.poll(1, TimeUnit.SECONDS) ?: continue
|
while (running) {
|
||||||
val value = el.value
|
val el = removalQueue.poll()
|
||||||
val now = Instant.now()
|
if(el == null) {
|
||||||
if (now > el.expiry) {
|
cond.await(1000, TimeUnit.MILLISECONDS)
|
||||||
val removed = map.remove(el.key, value)
|
continue
|
||||||
if (removed) {
|
}
|
||||||
updateSizeAfterRemoval(value.content)
|
val value = el.value
|
||||||
//Decrease the reference count for map
|
val now = Instant.now()
|
||||||
value.content.release()
|
if (now > el.expiry) {
|
||||||
|
val removed = map.remove(el.key, value)
|
||||||
|
if (removed) {
|
||||||
|
updateSizeAfterRemoval(value.content)
|
||||||
|
//Decrease the reference count for map
|
||||||
|
value.content.release()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
removalQueue.offer(el)
|
||||||
|
val interval = minOf(Duration.between(now, el.expiry), Duration.ofSeconds(1))
|
||||||
|
cond.await(interval.toMillis(), TimeUnit.MILLISECONDS)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
removalQueue.put(el)
|
|
||||||
Thread.sleep(minOf(Duration.between(now, el.expiry), Duration.ofSeconds(1)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
complete(null)
|
complete(null)
|
||||||
@@ -77,7 +86,7 @@ class InMemoryCache(
|
|||||||
|
|
||||||
fun removeEldest(): Long {
|
fun removeEldest(): Long {
|
||||||
while (true) {
|
while (true) {
|
||||||
val el = removalQueue.take()
|
val el = removalQueue.poll() ?: return mapSize
|
||||||
val value = el.value
|
val value = el.value
|
||||||
val removed = map.remove(el.key, value)
|
val removed = map.remove(el.key, value)
|
||||||
if (removed) {
|
if (removed) {
|
||||||
@@ -90,18 +99,22 @@ class InMemoryCache(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateSizeAfterRemoval(removed: ByteBuf): Long {
|
private fun updateSizeAfterRemoval(removed: ByteBuf): Long {
|
||||||
return size.updateAndGet { currentSize: Long ->
|
mapSize -= removed.readableBytes()
|
||||||
currentSize - removed.readableBytes()
|
return mapSize
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun asyncClose() : CompletableFuture<Void> {
|
override fun asyncClose() : CompletableFuture<Void> {
|
||||||
running = false
|
running = false
|
||||||
|
lock.writeLock().withLock {
|
||||||
|
cond.signal()
|
||||||
|
}
|
||||||
return closeFuture
|
return closeFuture
|
||||||
}
|
}
|
||||||
|
|
||||||
fun get(key: ByteArray) = map[CacheKey(key)]?.run {
|
fun get(key: ByteArray) = lock.readLock().withLock {
|
||||||
CacheEntry(metadata, content.retainedDuplicate())
|
map[CacheKey(key)]?.run {
|
||||||
|
CacheEntry(metadata, content.retainedDuplicate())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun put(
|
fun put(
|
||||||
@@ -109,18 +122,18 @@ class InMemoryCache(
|
|||||||
value: CacheEntry,
|
value: CacheEntry,
|
||||||
) {
|
) {
|
||||||
val cacheKey = CacheKey(key)
|
val cacheKey = CacheKey(key)
|
||||||
val oldSize = map.put(cacheKey, value)?.let { old ->
|
lock.writeLock().withLock {
|
||||||
val result = old.content.readableBytes()
|
val oldSize = map.put(cacheKey, value)?.let { old ->
|
||||||
old.content.release()
|
val result = old.content.readableBytes()
|
||||||
result
|
old.content.release()
|
||||||
} ?: 0
|
result
|
||||||
val delta = value.content.readableBytes() - oldSize
|
} ?: 0
|
||||||
var newSize = size.updateAndGet { currentSize: Long ->
|
val delta = value.content.readableBytes() - oldSize
|
||||||
currentSize + delta
|
mapSize += delta
|
||||||
}
|
removalQueue.offer(RemovalQueueElement(cacheKey, value, Instant.now().plus(maxAge)))
|
||||||
removalQueue.put(RemovalQueueElement(cacheKey, value, Instant.now().plus(maxAge)))
|
while (mapSize > maxSize) {
|
||||||
while (newSize > maxSize) {
|
removeEldest()
|
||||||
newSize = removeEldest()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user