Skip to content
Draft
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b8194f5
Initial plan
Copilot Mar 17, 2026
bc3c983
feat: add next-page prefetch cache for paginated SELECT queries
Copilot Mar 17, 2026
eb9ac07
refactor: improve thread naming and reduce duplication in executeQuer…
Copilot Mar 17, 2026
563e3f8
feat: materialise CLOB/NCLOB return columns in prefetch cache as String
Copilot Mar 17, 2026
77195b9
fix: datasource-isolated cache keys + background cleanup job for next…
Copilot Mar 17, 2026
268cdaa
fix: single shared static cleanup executor guarantees one background …
Copilot Mar 17, 2026
e953c30
feat(test): Postgres pagination cache integration test with BYTEA LOB…
Copilot Mar 17, 2026
73a4d18
fix(paging): use virtual thread for CLEANUP_EXECUTOR thread factory
Copilot Mar 17, 2026
a2a9726
feat(paging): per-datasource prefetchWaitTimeoutMs configuration
Copilot Mar 17, 2026
1d3d878
fix(jdbc): skip remote close() when ResultSet has no server-side UUID…
Copilot Mar 17, 2026
59ca642
fix(paging): fix Sonar issues — resource leaks, dead code, unused imp…
Copilot Mar 17, 2026
8c71a39
fix(paging): fix remaining Sonar issues — duplicate conditions, regex…
Copilot Mar 17, 2026
0293f8f
test: parameterize detection tests, add assertion, fix duplicate @Aft…
Copilot Mar 17, 2026
fab19cd
test: address review comments on test classes (round 2)
Copilot Mar 17, 2026
76f8de7
docs: add prefetch cache feature docs and refactor buildNextPageSql t…
Copilot Mar 17, 2026
27a02cd
feat: add per-datasource cache enabled flag with tests and docs update
Copilot Mar 18, 2026
37a84bf
refactor: per-datasource cache enabled is client-side property (ojp.n…
Copilot Mar 18, 2026
065b101
feat: add prefetch cache integration tests for MySQL, MariaDB, Cockro…
Copilot Mar 18, 2026
d7cf13c
fix(test): use getBoolean() for boolean-typed columns in pagination c…
Copilot Mar 18, 2026
4ba9354
fix: forward ojp.nextPageCache.* client properties to server; update …
Copilot Mar 19, 2026
3a93056
refactor(ci): merge prefetch-cache server into main OJP server; updat…
Copilot Mar 19, 2026
c0da110
docs: clarify two-tier config model for next-page prefetch cache acro…
Copilot Mar 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 111 additions & 14 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ jobs:
-e JAVA_TOOL_OPTIONS="-Dojp.server.slowQuerySegregation.enabled=true" \
rrobetti/ojp:0.4.1-SNAPSHOT

# Pagination-cache integration tests run against this server (port 10594)
- name: Start OJP Server container (prefetch cache on port 10594)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need a separated ojp server if now the nextPageCache is enabled in the client side per datasource?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No — the separate server is no longer needed. Since the cache opt-in is now a client-side property (ojp.nextPageCache.enabled), the main OJP server is all that's needed. Fixed in 3a93056: the nextPageCache flags (enabled, ttlSeconds, prefetchWaitTimeoutMs) are moved to the main server in all 8 CI jobs, the ojp-server-prefetch-cache container is removed, and all 7 pagination-cache CSV files are updated from port 105941059.

run: |
docker run -d --name ojp-server-prefetch-cache \
--network host \
-e JAVA_TOOL_OPTIONS="-Dojp.server.port=10594 -Dojp.prometheus.port=9164 -Dojp.server.slowQuerySegregation.enabled=true -Dojp.server.nextPageCache.enabled=true -Dojp.server.nextPageCache.ttlSeconds=60 -Dojp.server.nextPageCache.prefetchWaitTimeoutMs=5000" \
rrobetti/ojp:0.4.1-SNAPSHOT

- name: Wait for ojp-server to start
run: sleep 10

Expand All @@ -117,11 +125,15 @@ jobs:
# This is the key test step - only H2 tests are enabled
# All other database tests are disabled by default
- name: Test (ojp-jdbc-driver) with H2 only
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenableH2Tests=true
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenableH2Tests=true -DenableH2PrefetchCacheTests=true

- name: Show ojp-server.log
if: always() # ensures it runs even if previous steps fail
run: docker logs ojp-server 2>&1 || echo "ojp-server container not found"
run: |
docker logs ojp-server 2>&1 || echo "ojp-server container not found"
echo ""
echo "=== OJP Server (with prefetch cache) log ==="
docker logs ojp-server-prefetch-cache 2>&1 || echo "ojp-server-prefetch-cache container not found"

# ===========================================================================
# JOB 2: PostgreSQL Integration Tests
Expand Down Expand Up @@ -220,6 +232,15 @@ jobs:
-e JAVA_TOOL_OPTIONS="-Dojp.server.port=10593 -Dojp.prometheus.port=9163 -Dojp.server.slowQuerySegregation.enabled=true -Dojp.sql.enhancer.enabled=true -Dojp.sql.enhancer.mode=OPTIMIZE -Dojp.sql.enhancer.dialect=POSTGRESQL" \
rrobetti/ojp:0.4.1-SNAPSHOT

# Start third OJP server WITH next-page prefetch cache enabled
# Pagination-cache integration tests run against this server (port 10594)
- name: Start OJP Server container (prefetch cache on port 10594)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a separated ojp server if nextPageCache is not enabled in the client side?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No — a separate server is no longer needed. The main server has ojp.server.nextPageCache.enabled=true globally, and each client datasource opts in or out via ojp.nextPageCache.enabled in ojp.properties. The ojp-server-prefetch-cache container (port 10594) has been removed in 3a93056.

run: |
docker run -d --name ojp-server-prefetch-cache \
--network host \
-e JAVA_TOOL_OPTIONS="-Dojp.server.port=10594 -Dojp.prometheus.port=9164 -Dojp.server.slowQuerySegregation.enabled=true -Dojp.server.nextPageCache.enabled=true -Dojp.server.nextPageCache.ttlSeconds=60 -Dojp.server.nextPageCache.prefetchWaitTimeoutMs=5000" \
rrobetti/ojp:0.4.1-SNAPSHOT

- name: Wait for ojp-server to start
run: sleep 10

Expand All @@ -235,7 +256,7 @@ jobs:

# Run PostgreSQL-specific tests with -DenablePostgresTests flag
- name: Test (ojp-jdbc-driver) with PostgreSQL enabled
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenablePostgresTests=true
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenablePostgresTests=true -DenablePostgresPrefetchCacheTests=true

# ===================================================================
# SQL Enhancer Integration Test
Expand Down Expand Up @@ -267,6 +288,9 @@ jobs:
echo ""
echo "=== OJP Server (with SQL enhancer) log ==="
docker logs ojp-server-enhancer 2>&1 || echo "ojp-server-enhancer container not found"
echo ""
echo "=== OJP Server (with prefetch cache) log ==="
docker logs ojp-server-prefetch-cache 2>&1 || echo "ojp-server-prefetch-cache container not found"

# ===========================================================================
# JOB 3: MySQL Integration Tests
Expand Down Expand Up @@ -344,6 +368,14 @@ jobs:
-e JAVA_TOOL_OPTIONS="-Dojp.server.slowQuerySegregation.enabled=true" \
rrobetti/ojp:0.4.1-SNAPSHOT

# Pagination-cache integration tests run against this server (port 10594)
- name: Start OJP Server container (prefetch cache on port 10594)
run: |
docker run -d --name ojp-server-prefetch-cache \
--network host \
-e JAVA_TOOL_OPTIONS="-Dojp.server.port=10594 -Dojp.prometheus.port=9164 -Dojp.server.slowQuerySegregation.enabled=true -Dojp.server.nextPageCache.enabled=true -Dojp.server.nextPageCache.ttlSeconds=60 -Dojp.server.nextPageCache.prefetchWaitTimeoutMs=5000" \
rrobetti/ojp:0.4.1-SNAPSHOT

- name: Wait for ojp-server to start
run: sleep 10

Expand All @@ -359,11 +391,15 @@ jobs:

# Run MySQL-specific tests with -DenableMySQLTests flag
- name: Test (ojp-jdbc-driver) with MySQL enabled
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenableMySQLTests=true
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenableMySQLTests=true -DenableMySQLPrefetchCacheTests=true

- name: Show ojp-server.log
if: always()
run: docker logs ojp-server 2>&1 || echo "ojp-server container not found"
run: |
docker logs ojp-server 2>&1 || echo "ojp-server container not found"
echo ""
echo "=== OJP Server (with prefetch cache) log ==="
docker logs ojp-server-prefetch-cache 2>&1 || echo "ojp-server-prefetch-cache container not found"

# ===========================================================================
# ===========================================================================
Expand Down Expand Up @@ -440,6 +476,14 @@ jobs:
-e JAVA_TOOL_OPTIONS="-Dojp.server.slowQuerySegregation.enabled=true" \
rrobetti/ojp:0.4.1-SNAPSHOT

# Pagination-cache integration tests run against this server (port 10594)
- name: Start OJP Server container (prefetch cache on port 10594)
run: |
docker run -d --name ojp-server-prefetch-cache \
--network host \
-e JAVA_TOOL_OPTIONS="-Dojp.server.port=10594 -Dojp.prometheus.port=9164 -Dojp.server.slowQuerySegregation.enabled=true -Dojp.server.nextPageCache.enabled=true -Dojp.server.nextPageCache.ttlSeconds=60 -Dojp.server.nextPageCache.prefetchWaitTimeoutMs=5000" \
rrobetti/ojp:0.4.1-SNAPSHOT

- name: Wait for ojp-server to start
run: sleep 10

Expand All @@ -455,11 +499,15 @@ jobs:

# Run MariaDB-specific tests with -DenableMariaDBTests flag
- name: Test (ojp-jdbc-driver) with MariaDB enabled
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenableMariaDBTests=true
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenableMariaDBTests=true -DenableMariaDBPrefetchCacheTests=true

- name: Show ojp-server.log
if: always()
run: docker logs ojp-server 2>&1 || echo "ojp-server container not found"
run: |
docker logs ojp-server 2>&1 || echo "ojp-server container not found"
echo ""
echo "=== OJP Server (with prefetch cache) log ==="
docker logs ojp-server-prefetch-cache 2>&1 || echo "ojp-server-prefetch-cache container not found"

# ===========================================================================
# JOB 5: CockroachDB Integration Tests
Expand Down Expand Up @@ -527,6 +575,14 @@ jobs:
-e JAVA_TOOL_OPTIONS="-Dojp.server.slowQuerySegregation.enabled=true" \
rrobetti/ojp:0.4.1-SNAPSHOT

# Pagination-cache integration tests run against this server (port 10594)
- name: Start OJP Server container (prefetch cache on port 10594)
run: |
docker run -d --name ojp-server-prefetch-cache \
--network host \
-e JAVA_TOOL_OPTIONS="-Dojp.server.port=10594 -Dojp.prometheus.port=9164 -Dojp.server.slowQuerySegregation.enabled=true -Dojp.server.nextPageCache.enabled=true -Dojp.server.nextPageCache.ttlSeconds=60 -Dojp.server.nextPageCache.prefetchWaitTimeoutMs=5000" \
rrobetti/ojp:0.4.1-SNAPSHOT

- name: Wait for ojp-server to start
run: sleep 10

Expand All @@ -542,11 +598,15 @@ jobs:

# Run CockroachDB-specific tests with -DenableCockroachDBTests flag
- name: Test (ojp-jdbc-driver) with CockroachDB enabled
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenableCockroachDBTests=true
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenableCockroachDBTests=true -DenableCockroachDBPrefetchCacheTests=true

- name: Show ojp-server.log
if: always()
run: docker logs ojp-server 2>&1 || echo "ojp-server container not found"
run: |
docker logs ojp-server 2>&1 || echo "ojp-server container not found"
echo ""
echo "=== OJP Server (with prefetch cache) log ==="
docker logs ojp-server-prefetch-cache 2>&1 || echo "ojp-server-prefetch-cache container not found"

# ===========================================================================
# JOB 6: DB2 Integration Tests
Expand Down Expand Up @@ -695,6 +755,14 @@ jobs:
-e JAVA_TOOL_OPTIONS="-Dojp.server.slowQuerySegregation.enabled=true" \
rrobetti/ojp:0.4.1-SNAPSHOT

# Pagination-cache integration tests run against this server (port 10594)
- name: Start OJP Server container (prefetch cache on port 10594)
run: |
docker run -d --name ojp-server-prefetch-cache \
--network host \
-e JAVA_TOOL_OPTIONS="-Dojp.server.port=10594 -Dojp.prometheus.port=9164 -Dojp.server.slowQuerySegregation.enabled=true -Dojp.server.nextPageCache.enabled=true -Dojp.server.nextPageCache.ttlSeconds=60 -Dojp.server.nextPageCache.prefetchWaitTimeoutMs=5000" \
rrobetti/ojp:0.4.1-SNAPSHOT

- name: Wait for ojp-server to start
run: sleep 10

Expand All @@ -710,11 +778,15 @@ jobs:

# Run DB2-specific tests with -DenableDb2Tests flag
- name: Test (ojp-jdbc-driver) with DB2 enabled
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenableDb2Tests=true
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenableDb2Tests=true -DenableDb2PrefetchCacheTests=true

- name: Show ojp-server.log
if: always()
run: docker logs ojp-server 2>&1 || echo "ojp-server container not found"
run: |
docker logs ojp-server 2>&1 || echo "ojp-server container not found"
echo ""
echo "=== OJP Server (with prefetch cache) log ==="
docker logs ojp-server-prefetch-cache 2>&1 || echo "ojp-server-prefetch-cache container not found"

# ===========================================================================
# JOB 7: Multinode Integration Tests
Expand Down Expand Up @@ -1691,6 +1763,14 @@ jobs:
-e JAVA_TOOL_OPTIONS="-Dojp.server.slowQuerySegregation.enabled=true" \
rrobetti/ojp:0.4.1-SNAPSHOT

# Pagination-cache integration tests run against this server (port 10594)
- name: Start OJP Server container (prefetch cache on port 10594)
run: |
docker run -d --name ojp-server-prefetch-cache \
--network host \
-e JAVA_TOOL_OPTIONS="-Dojp.server.port=10594 -Dojp.prometheus.port=9164 -Dojp.server.slowQuerySegregation.enabled=true -Dojp.server.nextPageCache.enabled=true -Dojp.server.nextPageCache.ttlSeconds=60 -Dojp.server.nextPageCache.prefetchWaitTimeoutMs=5000" \
rrobetti/ojp:0.4.1-SNAPSHOT

- name: Wait for ojp-server to start
run: sleep 10

Expand All @@ -1706,11 +1786,15 @@ jobs:

# Run Oracle-specific tests with -DenableOracleTests flag
- name: Test (ojp-jdbc-driver) with Oracle enabled
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenableOracleTests=true
run: mvn test -pl ojp-jdbc-driver -Dgpg.skip=true -DenableOracleTests=true -DenableOraclePrefetchCacheTests=true

- name: Show ojp-server.log
if: always() # ensures it runs even if previous steps fail
run: docker logs ojp-server 2>&1 || echo "ojp-server container not found"
run: |
docker logs ojp-server 2>&1 || echo "ojp-server container not found"
echo ""
echo "=== OJP Server (with prefetch cache) log ==="
docker logs ojp-server-prefetch-cache 2>&1 || echo "ojp-server-prefetch-cache container not found"

# ===========================================================================
# JOB 10: SQL Server Integration Tests
Expand Down Expand Up @@ -1794,6 +1878,14 @@ jobs:
-e JAVA_TOOL_OPTIONS="-Dojp.server.slowQuerySegregation.enabled=true" \
rrobetti/ojp:0.4.1-SNAPSHOT

# Pagination-cache integration tests run against this server (port 10594)
- name: Start OJP Server container (prefetch cache on port 10594)
run: |
docker run -d --name ojp-server-prefetch-cache \
--network host \
-e JAVA_TOOL_OPTIONS="-Dojp.server.port=10594 -Dojp.prometheus.port=9164 -Dojp.server.slowQuerySegregation.enabled=true -Dojp.server.nextPageCache.enabled=true -Dojp.server.nextPageCache.ttlSeconds=60 -Dojp.server.nextPageCache.prefetchWaitTimeoutMs=5000" \
rrobetti/ojp:0.4.1-SNAPSHOT

- name: Wait for ojp-server to start
run: sleep 10

Expand All @@ -1814,11 +1906,16 @@ jobs:
run: |
mvn test -pl ojp-jdbc-driver -Dgpg.skip=true \
-DenableSqlServerTests=true \
-DenableSqlServerPrefetchCacheTests=true \
-Dtest="SQLServer*"

- name: Show ojp-server.log
if: always() # ensures it runs even if previous steps fail
run: docker logs ojp-server 2>&1 || echo "ojp-server container not found"
run: |
docker logs ojp-server 2>&1 || echo "ojp-server container not found"
echo ""
echo "=== OJP Server (with prefetch cache) log ==="
docker logs ojp-server-prefetch-cache 2>&1 || echo "ojp-server-prefetch-cache container not found"

# ===========================================================================
# JOB 11: Notify Integration Repository
Expand Down
8 changes: 8 additions & 0 deletions documents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ Located in [connection-pool/](connection-pool/):
Located in [analysis/](analysis/):
- [Transaction Isolation Handling](analysis/TRANSACTION_ISOLATION_HANDLING.md) - Complete technical documentation on transaction isolation reset behavior

## Features

Located in [features/](features/):
- [Next-Page Prefetch Cache](features/NEXT_PAGE_PREFETCH_CACHE.md) - Transparent background pre-fetching of the next query page to eliminate round-trip latency in paginated result sets
- [SQL Enhancer Engine Quickstart](features/SQL_ENHANCER_ENGINE_QUICKSTART.md) - SQL optimisation using Apache Calcite (experimental)
- [SQL Enhancer Configuration Examples](features/SQL_ENHANCER_CONFIGURATION_EXAMPLES.md) - Configuration examples for the SQL enhancer

## Database Setup Guides

Located in [environment-setup/](environment-setup/):
Expand Down Expand Up @@ -158,6 +165,7 @@ documents/
├── contributor-badges/ # Recognition program
├── designs/ # Design documents
├── environment-setup/ # Database setup guides
├── features/ # Feature guides and documentation
├── fixed-issues/ # Issue fix documentation
├── guides/ # Developer guides
├── images/ # Diagrams and images
Expand Down
65 changes: 65 additions & 0 deletions documents/configuration/ojp-server-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,71 @@ For full integration examples including Docker Compose setups, see the **[Teleme
| `ojp.server.slowQuerySegregation.slowSlotTimeout` | `OJP_SERVER_SLOWQUERYSEGREGATION_SLOWSLOTTIMEOUT` | long | 120000 | Timeout for acquiring slow operation slots (ms) | 0.2.0-beta |
| `ojp.server.slowQuerySegregation.fastSlotTimeout` | `OJP_SERVER_SLOWQUERYSEGREGATION_FASTSLOTTIMEOUT` | long | 60000 | Timeout for acquiring fast operation slots (ms) | 0.2.0-beta |

### Next-Page Prefetch Cache Settings

The prefetch cache transparently pre-executes the **next page query** in the background while the current page is being sent to the client. When the client requests the next page, the rows are served from memory instead of hitting the database again, significantly reducing perceived latency for paginated result sets.

The cache detects SQL pagination clauses automatically (`LIMIT/OFFSET`, `OFFSET … FETCH`, `FETCH FIRST … ROWS ONLY`, MySQL `LIMIT m, n`, and standalone `LIMIT n`). No client changes are needed — the feature is entirely transparent.

| Property | Environment Variable | Type | Default | Description | Since |
|---|---|---|---|---|---|
| `ojp.server.nextPageCache.enabled` | `OJP_SERVER_NEXTPAGECACHE_ENABLED` | boolean | false | Enable/disable the next-page prefetch cache globally | 0.4.1 |
| `ojp.server.nextPageCache.ttlSeconds` | `OJP_SERVER_NEXTPAGECACHE_TTLSECONDS` | long | 60 | Maximum time (seconds) a cached page is kept before being discarded | 0.4.1 |
| `ojp.server.nextPageCache.maxEntries` | `OJP_SERVER_NEXTPAGECACHE_MAXENTRIES` | int | 100 | Maximum number of cache entries across all datasources | 0.4.1 |
| `ojp.server.nextPageCache.prefetchWaitTimeoutMs` | `OJP_SERVER_NEXTPAGECACHE_PREFETCHWAITTIMEOUTMS` | long | 5000 | Maximum time (ms) to wait for a prefetch to complete before falling back to a live query | 0.4.1 |
| `ojp.server.nextPageCache.cleanupIntervalSeconds` | `OJP_SERVER_NEXTPAGECACHE_CLEANUPINTERVALSECONDS` | long | 60 | Interval (seconds) at which the background cleanup thread evicts expired entries | 0.4.1 |
| `ojp.server.nextPageCache.datasource.<name>.prefetchWaitTimeoutMs` | *(no env-var equivalent)* | long | *(global default)* | Per-datasource override for `prefetchWaitTimeoutMs`; `<name>` matches `ojp.datasource.name` on the client | 0.4.1 |

> **Per-datasource `enabled` is a client-side setting.**
> Each datasource in the client application can independently opt in or out of the prefetch cache
> by setting `ojp.nextPageCache.enabled=false` in its `ojp.properties`:
> ```properties
> # ojp.properties — client application
> # Disable the prefetch cache for the "random-access" datasource
> random-access.ojp.nextPageCache.enabled=false
> ```

#### Next-Page Prefetch Cache Configuration Examples

**Enable the cache with default settings:**
```bash
java -Duser.timezone=UTC \
-Dojp.server.nextPageCache.enabled=true \
-jar ojp-server.jar
```

**Enable with custom TTL and wait timeout:**
```bash
java -Duser.timezone=UTC \
-Dojp.server.nextPageCache.enabled=true \
-Dojp.server.nextPageCache.ttlSeconds=30 \
-Dojp.server.nextPageCache.prefetchWaitTimeoutMs=2000 \
-jar ojp-server.jar
```

**Per-datasource wait timeout override (server-side):**
```bash
# Give the "analytics" datasource more time to prefetch large pages
java -Duser.timezone=UTC \
-Dojp.server.nextPageCache.enabled=true \
-Dojp.server.nextPageCache.prefetchWaitTimeoutMs=2000 \
-D"ojp.server.nextPageCache.datasource.analytics.prefetchWaitTimeoutMs=10000" \
-jar ojp-server.jar
```

**Via environment variables:**
```bash
export OJP_SERVER_NEXTPAGECACHE_ENABLED=true
export OJP_SERVER_NEXTPAGECACHE_TTLSECONDS=60
export OJP_SERVER_NEXTPAGECACHE_PREFETCHWAITTIMEOUTMS=5000
export OJP_SERVER_NEXTPAGECACHE_CLEANUPINTERVALSECONDS=60
java -Duser.timezone=UTC -jar ojp-server.jar
```

> **ℹ️ Cache isolation**: Entries are keyed by `datasourceId + normalizedSQL`, so two datasources executing the same query never share cached data.

> **ℹ️ Background cleanup**: A single shared virtual thread (`ojp-prefetch-cache-cleanup`) runs the eviction scan at the configured interval. No additional threads are created regardless of how many datasources are active.

### SQL Enhancer and Schema Loader Settings

> **⚠️ EXPERIMENTAL FEATURE - NOT RECOMMENDED FOR PRODUCTION**
Expand Down
Loading
Loading