Modernize the toolchain: Scala 2.13.16, JDK 21, Apache Pekko, and wider PostgreSQL support - #1383
Draft
GeekOfWires wants to merge 10 commits into
Draft
Modernize the toolchain: Scala 2.13.16, JDK 21, Apache Pekko, and wider PostgreSQL support#1383GeekOfWires wants to merge 10 commits into
GeekOfWires wants to merge 10 commits into
Conversation
Toolchain: - Scala 2.13.10 -> 2.13.16 - sbt 1.8.2 -> 1.10.7 (needed for JDK 21 in a later phase) Libraries (patch/minor only, no slf4j-2 / cross-major boundary crossings): - specs2 4.20.9, scalatest 3.2.19, logback 1.2.13, jansi 2.4.1, nscala-time 2.34.0, json4s 4.0.7, config 1.4.3, pureconfig 0.17.7, enumeratum 1.7.3, commons-io 2.16.1, sentry 6.34.0, circe 0.14.9 Scala 2.13.16 source/tooling fixes (so the bump actually builds): - Zones.scala: annotate the anonymous-subclass `val zone` with an explicit `: Zone` type; 2.13.16 rejects the recursive value (its body references it). - semanticdb disabled: sbt-scalafix 0.10.4 pins semanticdb 4.6.0, which has no 2.13.16 build; re-enable with a scalafix bump that supplies a compatible semanticdb (linter only; does not affect compilation or tests). - sbt plugins left at their existing versions (earlier speculative bumps did not resolve; plugin currency is out of scope for this branch). Deferred to later phases: Akka->Pekko, postgres/flyway/quill, bcprov jdk15on->jdk18on, and the coupled scodec-2 / scala-graph-2 / logback-1.5 bumps. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pekko is the Apache-2.0 licensed fork of Akka 2.6.x; a drop-in namespace/
coordinate migration across ALL source roots (src/, server/, tools/).
- build.sbt: com.typesafe.akka:*:2.6.20 -> org.apache.pekko:*:1.1.3
(pekko-http 10.2.10 -> 1.1.0); removed the "do not upgrade (BSL)" pin.
- Source: akka.* -> org.apache.pekko.* across root src/, the server/ subproject,
and tools/ (actor/util/pattern/io/event/dispatch/stream/testkit + all imports),
including Server.scala's aliased `import akka.{actor => classic}`.
- Two files in the actor package to reach internals moved akka.actor ->
org.apache.pekko.actor: MDCContextAware (protected[akka] aroundReceive ->
protected[pekko]) and MDCPropagatingExecutionContext.
- Vendored scodec<->ByteString interop renamed scodec.interop.akka ->
scodec.interop.pekko (Scala package object + the Java PrivacyHelper reaching
Pekko's bytecode-public ByteString1C) and its 3 importers.
- Config: akka.conf -> pekko.conf; namespace akka{} -> pekko{}, logger class
values -> org.apache.pekko.*; application.conf include; test config paths
akka.* -> pekko.* (root + server ActorTest).
- Two server tests (AutoRepair{,Integration}Test): the cross-package relative
`import base.FreedContextActorTest` absolutized to
`import actor.base.FreedContextActorTest` (Scala 2.13.16 no longer resolves
the relative-sibling form).
Verified: all source roots compile (root + server + tools) on Pekko 1.1.3.
Follow-ups (not blocking): unused pekko-stream/http/cluster-*/protobuf-v3 kept
for parity; Kamon still carries Akka instrumentation (add kamon-pekko later,
kanela no-ops on absent classes).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Dockerfile builder: JDK 11 image -> sbtscala JDK 21 image (exact tag flagged in a comment to confirm on Docker Hub; the project's Scala/sbt come from build.sbt/build.properties, not the image). - Dockerfile runtime: openjdk:18-slim -> eclipse-temurin:21-jre (openjdk images are deprecated; this also aligns build and runtime on one JDK, fixing the prior 11/18 split). - CI (test.yaml): coursier jvm adopt:11 -> temurin:21. - bcprov-jdk15on 1.70 -> bcprov-jdk18on 1.78.1 (jdk15on is the legacy artifact name; jdk18on is the modern-JDK build; API-compatible). JDK 21 is supported now that the actor toolkit is Pekko 1.1 (JDK 8/11/17/21); Akka 2.6 was only supported through JDK 17. Not built in-session (no Docker/sbt); confirm the builder image tag, then build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Driver/migration stack (enabled by the JDK 21 move):
- postgresql JDBC 42.5.4 -> 42.7.4 (spans PG12..18; still supports the current
12.8 CI / 14 compose servers).
- Flyway 9.16.0 -> 10.20.1 + the now-separate flyway-database-postgresql module
(Flyway 10 is modular). 10.x supports PG16/17 and requires JDK 17+, which Phase 2
provides. Server.scala already uses the 10-compatible configure()/load() API.
- Config.toJdbc now passes the configured sslmode to the Flyway/JDBC connection so
it matches the jasync runtime path (they previously negotiated TLS differently).
Prove-and-preserve, not a version swap:
- docker-compose postgres image parameterized: postgres:${POSTGRES_VERSION:-14}.
- CI runs a PostgreSQL matrix [12.8, 16, 17] (fail-fast off) so migrations + tests
are proven on newer majors while the current 12.8 stays green.
Deliberately NOT changed:
- Existing V001..V017 migrations are untouched -- editing an applied migration breaks
Flyway checksum validation. The legacy `EXECUTE PROCEDURE` trigger syntax still works
through PG17; modernizing to `EXECUTE FUNCTION` needs a new migration (follow-up).
- Runtime driver quill-jasync 3.18.0 / jasync 1.2.3 kept as-is; the CI matrix will
surface whether a jasync 2.x + matching Quill bump is actually needed for newer PG,
rather than bumping the fragile pairing blind.
Not built in-session (sbt unavailable); run the matrix to validate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LocalActionTest references Default.GUID0 but never imported net.psforever.objects.Default (unlike its sibling tests). This is a pre-existing gap in the base psforever#1379 commit, surfaced when the test sources were compiled during the modernization verification -- not a modernization change. Adds the import so the test suite compiles. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
….5.18 The JDK 21 move (Phase 2) was verified for compile and test but not for `server/run`, which attaches the Kanela instrumentation agent. On JDK 21 the old agent aborted startup: Unable to start Kanela Agent ... Failed to resolve the class file version of the current VM: Unknown Java version: 0 kamon-bundle 2.6.1 / sbt-kanela-runner 2.0.14 ship Kanela 1.0.11, whose ByteBuddy predates JDK 21. Bumping to kamon-bundle/apm-reporter 2.7.7 and sbt-kanela-runner 2.1.1 pulls Kanela 1.0.18 (JDK 21-aware) with a matching instrumentation API. kamon 2.7.7's dependency tree then resolves slf4j-api 2.0.x, which ignores logback-classic 1.2.13's 1.7-era binding (NOPLoggerFactory -> ClassCastException at Server.scala when casting to logback's LoggerContext). Bumping logback-classic to 1.5.18 provides the slf4j 2.0 service provider. Verified: full test suite unchanged (192 pass / 38 fail scalatest + 3 specs2 -- the pre-existing psforever#1379 baseline, no new regressions) and the server boots on JDK 21 (Pekko Slf4jLogger started, Kamon module registry active). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The JDK 21 sbt builder tag used by the Dockerfile and docker-compose (eclipse-temurin-jammy-21.0.5_11_1.10.7_2.13.16) is not published on Docker Hub, so image pulls failed. No sbtscala/scala-sbt tag bundles sbt 1.10.7 + Scala 2.13.16; the launcher fetches those from build.properties/build.sbt regardless, so the image only needs to provide JDK 21 + an sbt launcher. Switch both to the newest available JDK 21 tag (21.0.2_13_1.10.4_2.13.15). Also update docker-compose: - loginserver was still on the pre-modernization JDK 8 / Scala 2.13.10 image, which cannot build the Pekko/JDK 21 code; move it to the JDK 21 image. - default the db service to postgres:18 (newest major) via POSTGRES_VERSION. Verified end to end: the full stack boots, the server auto-migrates a fresh PostgreSQL 18.4 database (Flyway applies all 17 migrations; Flyway 10.20.1 emits only a soft "newer than tested" warning), and the login server listens. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Brings in upstream through 2f63d1c, most notably the server hitch reduction work (psforever#1386), the PropertyOverrideManager revert (psforever#1388) and the LLU/zone-lock fixes (psforever#1387). Conflict resolutions: - pekko.conf: psforever#1386 added `akka.actor.default-dispatcher` and `akka.scheduler` blocks. Git followed the akka.conf -> pekko.conf rename and merged them textually, which would have left both blocks under an `akka.*` prefix that Pekko never reads -- silently discarding the default-dispatcher sizing and the scheduler wheel tuning. Renamed both to `pekko.*` and updated the surrounding prose. - SocketPane / InterstellarClusterService: kept the Pekko namespace and took upstream's added `DispatcherSelector` import. - ContainableBehavior: took upstream's fix that drops the duplicate Resume(). That removed the only use of AskTimeoutException, so the import goes with it. - docker-compose.yml: kept the JDK 21 image this branch moved to, and carried over upstream's rationale for keeping the compose JVM aligned with the Dockerfile's. - SocketActor / SocketPane comments: the spawn-site dispatcher comments from psforever#1386 refer to the config file by name; updated akka.conf -> pekko.conf. Verified: `sbt compile` is green on JDK 21, upstream's drain-limit config, spawn-site dispatchers and V018 index migration all survive the merge, and the tree carries no Akka references outside the pre-existing `AkkaActor` alias in Default.scala. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Scala 2.13.18 is the current 2.13 release; the branch sat on 2.13.16.
sbt-scoverage 2.0.7 -> 2.4.4
scalac-scoverage-plugin is cross-published against the *full* Scala
version, and 2.0.7 pins a compiler plugin whose builds stop at
2.13.10. That is not a new problem introduced by this bump: CI has
been red on this PR since it opened, and every test job dies in the
same place before running a single test --
ResolveException: Error downloading
org.scoverage:scalac-scoverage-plugin_2.13.16:2.0.8 -- Not found
2.4.4 supplies a compiler plugin published for 2.13.18. Verified
locally: `sbt coverage Test/compile` now resolves and reaches
"Beginning coverage instrumentation", which is the step CI never got
past.
sbt-scalafix 0.10.4 -> 0.14.7, semanticdb re-enabled
The branch disabled semanticdb because 0.10.4 pins semanticdb 4.6.0,
which has no 2.13.16 build, and left a note to re-enable it alongside
a scalafix bump. 0.14.7 pins scalameta 4.17.0, which does publish
semanticdb-scalac for 2.13.18, so that note is now discharged and the
linter works again. This is the one separable piece of this commit:
CI does not run scalafix, so it can be dropped without affecting the
build.
Docs and images
README still documented JDK 8 / Scala 2.13.3 / sbt 1.4.5 and told
deployers they needed a Java 8 JRE -- stale since the JDK 21 move, and
it documents exactly the toolchain this PR modernizes. Brought in line
with the branch (JDK 21, 2.13.18, sbt 1.10.7, PostgreSQL 12+ with the
CI matrix and the compose default noted). Dockerfile/docker-compose
version comments updated to match.
Deliberately not changed: sbt stays at 1.10.7. Moving to the 1.12 line
is a build-tool change with its own risk and is not required by Scala
2.13.18.
Verification on JDK 21: compile is green, and the test suite shows no
regression against the pre-merge branch. That branch already fails 40 of
1941 tests on this Windows machine; 2.13.18 shows the same 33-suite core
plus members of the EventServiceCacheSupport family, which asserts on
50-250ms wall-clock windows -- all 6 of its suites fail in isolation
here, and which subset surfaces in a full run varies with machine load.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Completes the toolchain bump alongside Scala 2.13.18. Not required by 2.13.18 -- kept as its own commit so it can be reverted independently of the Scala work. The risk here is plugin compatibility rather than the compiler, so that is what was checked: the build definition loads under 1.12.14 with all five plugins unchanged (sbt-pack 0.17, sbt-scoverage 2.4.4, sbt-kanela-runner 2.1.1, sbt-scalafmt 2.5.0, sbt-scalafix 0.14.7), and `coverage` still reaches "Beginning coverage instrumentation". Verified on JDK 21: `sbtVersion` reports 1.12.14, `scalaVersion` 2.13.18, compile and instrumented Test/compile are green, and the test suite produces a failing-suite set byte-identical to the run before this bump -- so it changes nothing behaviourally. sbt 2.0.x is deliberately not adopted: it is a major version with build syntax changes, and nothing here needs it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Process Proposal
This PR exists primarily to give the project visibility into the full process of making
the changeover, shown as one continuous path rather than a polished end state. It is
admittedly larger than a comfortable single merge, and would reasonably be broken up
before landing.
It is presented here as a single pull request because the changes are interdependent:
getting from Akka-runnable on the earlier JDK to Pekko-runnable on the latest JDK took
several linked steps — toolchain bump → Akka→Pekko migration → JDK 21 → the runtime and
dependency fixes that only surface once the server actually boots — and no single step is
runnable on its own without the others. Keeping them together documents the real path taken
and lets reviewers see exactly what each hop required, before deciding how to slice it for
merge.
Summary
This branch modernizes the build/runtime stack in small, individually-reviewable phases,
each in its own commit. Nothing in game logic or the network protocol changes — this is
purely infrastructure. It compiles green, runs on JDK 21, and introduces zero test
regressions (evidence below).
Phase 0: bump Scala, sbt, and low-risk dependenciesPhase 1: migrate Akka 2.6.20 -> Apache Pekko 1.1.xakka.*→org.apache.pekko.*(pekko-actor 1.1.3, pekko-http 1.1.0),akka.conf→pekko.conf, config namespaceakka { }→pekko { }Phase 2: move build/runtime/CI to JDK 21eclipse-temurin:21-jreruntime, CI JVM → temurin 21, bcprov-jdk18on 1.78.1Phase 3: widen PostgreSQL version supportAdd missing Default import in LocalActionTestmaster(see below)Enable server runtime on JDK 21: Kamon 2.7.7, Kanela 2.1.1, logback 1.5.18Fix Docker image tag; run compose stack on JDK 21 / Postgres 18Why
2.7.0; the project is pinned to the last Apache-2.0 Akka (2.6.20). Apache Pekko 1.x is the
community fork of Akka 2.6.x under Apache-2.0 and is a near drop-in (namespace change only).
Migrating now keeps the actor layer on a permissively-licensed, actively-maintained base and
unblocks future actor/stream/HTTP upgrades.
actively supported; the previous sbt 1.8.2 / Scala 2.13.10 combination will not even start
on JDK 21.
PostgreSQL majors (verified up to 18) while the CI matrix keeps proving the current 12.8
still migrates — no forced deprecation.
Verification
sbt clean testcompiles all source roots and runs the full suite on JDK 21.Slf4jLoggerstarts, Flyway applies all 17 migrations to a fresh PostgreSQL 18.4database, and the login server listens. (Flyway 10.20.1 emits only a soft "newer than
tested" warning on PG 18 and migrates successfully.)
Test status — please read
The suite currently reports 192 pass / 38 fail (scalatest) + 3 specs2 failures. These are
not introduced by this branch — they reproduce identically on today's
master. I confirmedthis with a full attribution matrix:
master(unchanged)Same count, same suites across every axis → the Pekko/JDK/Scala changes flip none of them.
The failing suites are timing-sensitive
objects.*ActorTests (theActorTestbase classdocuments this flakiness) tied to the message bundling introduced in #1379; a same-toolchain
Akka baseline reproduces them identically. Happy to share the full per-suite breakdown.
Separately, the
Add missing Default import in LocalActionTestcommit fixes a spot wheremaster's test sources don't currently compile (LocalActionTestreferencesDefault.GUID0without importing
net.psforever.objects.Default).Deliberately conservative / follow-ups
pekko-stream/pekko-http/ cluster modules are kept for parity but have no directimports yet — safe removal candidates in a follow-up.
sbt-scalafixis temporarily disabled (semanticdbEnabled := false) because 0.10.4 pins asemanticdb with no 2.13.16 build; re-enable alongside a scalafix bump.
kamon-pekkoinstrumentation and a jasync/Quill bump are left for a later pass.Reviewing commit-by-commit is recommended — each phase stands on its own and can be split into
separate PRs if that's preferred.
Prepared with assistance from Claude (Opus 4.8); commits carry
Co-Authored-Bytrailers accordingly.