Conversation
…estrict
- listAdminGames gains geoAvailableCountries: games with no game or provider
geo rule for any listed country; empty page when a listed country is
blocked platform-wide. Guarded by compliance:view like the other geo filters.
- GET /compliance/blocked-countries lists the platform-wide block set.
- POST /compliance/game-geo-rules/bulk/{restrict,unrestrict} write one
country's game rule across gameIds and every game of providerIds in one
transaction, with a 5,000-game cap, idempotent, unknown ids in notFound, never
touches provider rules. Guarded by compliance:manage-geo.
- Bulk writes take one exclusive per-country advisory lock; single-game
writes take it shared ahead of their per-(game, country) keys.
- Bulk audit rows are appended inside the transaction in one batch via the
new AuditWritePort.recordEventsInTransaction; the post-commit events carry
auditRecorded so the subscriber does not write them again.
- GameGeoCheckPort gains a required listGloballyBlockedCountries().
Bulk restrict and unrestrict emit one compliance.game-geo-rules.bulk_updated event per call, carrying the rules the call added or removed, and audit records it as one row with before and after state, as gaming bulk does. This drops the batched in-transaction audit writer and its sealed port method. The shared-lock helper folds into withAdvisoryXactLocks as a mode argument.
okapitula
marked this pull request as ready for review
September 25, 2026 09:45
okapitula
requested review from
damianrzepka,
jakubfilinger-b,
klaudia-blazyczek-blurify,
marek-chmielowski-blurify,
mp-blurify and
zaxovaiko
as code owners
September 25, 2026 09:45
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.
Summary
The admin games list can filter to games available in a country, and a country's game geo rule can be restricted or unrestricted for many games in one atomic, audited call.
GET /backoffice/gaming/gamesgainsgeoAvailableCountries(up to 50,compliance:view). A game matches when neither it nor its provider has a geo rule for any listed country (NOT EXISTSanti-joins, same semantics asComplianceService.checkGame). If a listed country is blocked platform-wide, the page is empty. Rejected withgeoBlocked=trueor when it shares a code withgeoBlockedCountries.GET /compliance/blocked-countries(compliance:view) returns the platform-wide block set (igaming.blockedCountriespluscountry_rulerows with actionblock), so the backoffice can explain an empty result.POST /compliance/game-geo-rules/bulk/restrictand.../bulk/unrestrict(compliance:manage-geo) take{ gameIds?, providerIds?, countryCode, reason }, with the same target caps as the gaming bulk routes.providerIdsexpands to every game of the provider. One transaction, 5,000-game cap checked before any write, idempotent (unchanged games are skipped, no NOT_FOUND), unknown ids reported innotFound. Game rules only; provider rules are never touched. Unrestrict also returnsstillBlockedByProviderandgloballyBlocked.compliance.game-geo-rules.bulk_updatedevent after commit (operation,countryCode,reason,target,notFound, andrules: the full rule rows the call added or removed). Audit records it as one row, as it recordsgaming.games.bulk_updated, withresourceType: 'game-geo-rule'andresourceId: null.before.rulesis empty andafter.rulesholds the new rules.before.rulesholds the removed rules, with their ids, original reasons and timestamps;after.rulesis empty.compliance.game-geo-rule.upserted/.deletedevents the single-game routes emit. The single-game routes are unchanged.Why
The backoffice needs "Available in X" alongside the existing "Restricted in X" (
geoBlockedCountries), and bulk-bar Restrict / Unrestrict for one country. Until now, geo rules could only be written one game at a time.Alternatives considered
max_locks_per_transactionx connections) and fail unrelated transactions. Instead, bulk writes take one exclusive per-country key, and single-game writes take it shared ahead of their per-(game, country) keys. Single-game writes for a country still run concurrently; a bulk write and single-game writes for the same country take turns.game-geo-rule.upserted/.deletedevent per changed game, as the single-game routes emit: up to 5,000 fire-and-forget audit writes, each taking the globalaudit_loglock in its own transaction, would starve the connection pool. Writing them in one batch inside the bulk transaction would need a new method on the sealed audit port.emitInTransaction): the audit record would then commit with the rules instead of being published best-effort after commit. That would require the outbox to be enabled (OUTBOX_ENABLED), and no core path usesemitInTransactiontoday. The single-game geo routes and the gaming bulk routes also audit after commit.country_ruleand the igaming config itself for the global-block check: this would put the definition of "globally blocked" in two places. It is a port method instead, which also backs the new read.Risks
GameGeoCheckPortgains a required method,listGloballyBlockedCountries(). Only compliance bindsGAME_GEO_CHECKin core; a consumer that binds its own adapter must implement it. An adapter that returns an empty list makesgeoAvailableCountriesshow globally-blocked markets as available. The play-timecheckGamegate is unaffected.resourceId/q), because the bulk row hasresourceId: null. It is found by filtering onaction=compliance.game-geo-rules.bulk_updatedand readingbefore.rules/after.rules, as with gaming bulk.providerIdsscope is a snapshot at call time: a game moved onto the provider concurrently is not covered. Re-running the call is idempotent and closes the gap.reasonis capped at 500 characters. The single-game routes are unchanged.