In ncs-stage.yaml fehlen die tournament-Einträge in den beiden Production-Stages:
- eu-central-1-prod
- htwg-1-prod
Das sind die zwei Blöcke die ergänzt werden müssen.
✻ Worked…
What was done right
- ncs-warehouse.yaml — new tournament Warehouse added, identical config to all other services (same interval, SemVer, strictSemvers, discoveryLimit, correct…
What was done right
- ncs-warehouse.yaml — new tournament Warehouse added, identical config to all other services (same interval, SemVer, strictSemvers, discoveryLimit, correct image path…
Overall the feature direction is solid — the Swiss pairing algorithm, NDJSON streaming design, and the shift from polling to reactive Redis
null used as a sentinel. Per project style, this should be Option(watchedGames.get(gameId)).flatMap(m => Option(m.get(turn))). Using null here is inconsistent with the Option/Either convention and risks an NPE if the code changes. is inconsistent with the Option/Either convention and risks an NPE if the code changes.
The comment above says "subscribe must be issued non-blocking from this context". If onSubscribed calls sendConnected (which publishes to Redis), and this all happens on the Vert.x event loop thread, the publish in sendConnected could block the loop if the Redis client drains its write buffer. Ensure sendConnected is also non-blocking (it appears to use .subscribe().with(...) which is fine, but verify the pubsub publish pipeline).
There is a TOCTOU race: two gameStart events for the same game (one white, one black) can arrive concurrently. Both may see existing == null and call subscribeAndConnect twice, resulting in duplicate subscriptions and duplicate moves. The putIfAbsent + null-check pattern is correct in principle, but subscribeAndConnect must be inside the same atomic block, or a separate AtomicBoolean guard should be used.
Every replica of the official-bots service will receive every bot event. As bot and game counts grow, this is a hot fan-out path with no deduplication. If two replicas are running, both will try to compute a move and publish it — the second publish wins non-deterministically. Add a distributed lock (e.g., Redis SET NX) per (gameId, turn) before computing a move.
The engine name is hardcoded but the engine instance (ClassicalBot) is also hardcoded at class init. If another difficulty is injected in the future this will be invisible. Either derive the name from the Bot instance (if Bot exposes a name), or at least make it a constant rather than a method.