Remove race condition in workspace initialisation if iam-svc is up (#867)

Remove race condition in workspace initialisation if iam-svc is up
before config-svc.

iam.py — handle_create_workspace:
- Config registration (_on_workspace_created) moves before the IAM
  table write, so it's a prerequisite. If the config put fails,
  the exception propagates and the IAM create doesn't happen.
- On duplicate, the IAM table write is skipped but config
  registration still runs (idempotent put). Returns the existing
  record with no error instead of returning _err("duplicate", ...).

service.py — _announce_workspace_created → _ensure_workspace_registered:
- Renamed to reflect the new semantics.
- Exceptions propagate instead of being swallowed — if config
  registration fails, the caller sees the error.
This commit is contained in:
cybermaggedon 2026-05-06 21:21:48 +01:00 committed by GitHub
parent d282d72db1
commit fe542b3d33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 20 additions and 28 deletions

View file

@ -901,12 +901,13 @@ class IamService:
"workspace ids beginning with '_' are reserved", "workspace ids beginning with '_' are reserved",
) )
if self._on_workspace_created:
await self._on_workspace_created(v.workspace_record.id)
existing = await self.table_store.get_workspace( existing = await self.table_store.get_workspace(
v.workspace_record.id, v.workspace_record.id,
) )
if existing is not None: if existing is None:
return _err("duplicate", "workspace already exists")
now = _now_dt() now = _now_dt()
await self.table_store.put_workspace( await self.table_store.put_workspace(
id=v.workspace_record.id, id=v.workspace_record.id,
@ -915,9 +916,6 @@ class IamService:
created=now, created=now,
) )
if self._on_workspace_created:
await self._on_workspace_created(v.workspace_record.id)
row = await self.table_store.get_workspace(v.workspace_record.id) row = await self.table_store.get_workspace(v.workspace_record.id)
return IamResponse(workspace=self._row_to_workspace_record(row)) return IamResponse(workspace=self._row_to_workspace_record(row))

View file

@ -151,7 +151,7 @@ class Processor(AsyncProcessor):
keyspace=keyspace, keyspace=keyspace,
bootstrap_mode=self.bootstrap_mode, bootstrap_mode=self.bootstrap_mode,
bootstrap_token=self.bootstrap_token, bootstrap_token=self.bootstrap_token,
on_workspace_created=self._announce_workspace_created, on_workspace_created=self._ensure_workspace_registered,
on_workspace_deleted=self._announce_workspace_deleted, on_workspace_deleted=self._announce_workspace_deleted,
) )
@ -218,19 +218,13 @@ class Processor(AsyncProcessor):
finally: finally:
await client.stop() await client.stop()
async def _announce_workspace_created(self, workspace_id): async def _ensure_workspace_registered(self, workspace_id):
try:
await self._config_put( await self._config_put(
"__workspaces__", "workspace", workspace_id, "__workspaces__", "workspace", workspace_id,
'{"enabled": true}', '{"enabled": true}',
) )
logger.info( logger.info(
f"Announced workspace creation: {workspace_id}" f"Registered workspace in config: {workspace_id}"
)
except Exception as e:
logger.error(
f"Failed to announce workspace creation "
f"{workspace_id}: {e}", exc_info=True,
) )
async def _announce_workspace_deleted(self, workspace_id): async def _announce_workspace_deleted(self, workspace_id):