Skip to content

feat(tenant): deactivate all agents when a tenant is disabled - #522

Closed
Diyanada99x wants to merge 3 commits into
mainfrom
518-deactivating-a-tenant-should-bring-down-all-the-agents-into-an-deactivated-state
Closed

Diyanada99x wants to merge 3 commits into
mainfrom
518-deactivating-a-tenant-should-bring-down-all-the-agents-into-an-deactivated-state

Conversation

@Diyanada99x

Copy link
Copy Markdown
Collaborator

Disabling a tenant only flipped Tenant.Enabled and emitted tenant.disabled; its activations stayed active, Temporal workflows kept running and schedules kept firing.

UpdateTenant now deactivates every active activation of the tenant whenever enabled=false is sent, one at a time, via ActivationService.DeactivateAgentAsync (cancel/terminate workflows, delete schedules, mark inactive).

Disabling a tenant only flipped Tenant.Enabled and emitted
tenant.disabled; its activations stayed active, Temporal workflows
kept running and schedules kept firing.

UpdateTenant now deactivates every active activation of the tenant
whenever enabled=false is sent, one at a time, via
ActivationService.DeactivateAgentAsync (cancel/terminate workflows,
delete schedules, mark inactive).
@Diyanada99x Diyanada99x added the ai-dlc/pr/pr-review Ready for AI-DLC PR review label Sep 29, 2026
@hasith

hasith commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Review in Progress

Claude Code is analyzing this pull request. The review will be posted here shortly.

PR Reviewer (1.9.9)

Comment thread XiansAi.Server.Src/Shared/Services/TenantService.cs Fixed
Comment thread XiansAi.Server.Src/Shared/Services/TenantService.cs Fixed
 into 518-deactivating-a-tenant-should-bring-down-all-the-agents-into-an-deactivated-state

@hasith hasith left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PR Review Report

PR: feat(tenant): deactivate all agents when a tenant is disabled
Author: Diyanada99x
Files Changed: 3 | +84 / -2
Verdict: NEEDS DISCUSSION


Summary

This PR closes a real gap: disabling a tenant previously left its agent activations (Temporal workflows/schedules) running. UpdateTenant now walks active activations and deactivates them one at a time via ActivationService.DeactivateAgentAsync, with best-effort error handling and an audit-message summary. The core logic is sound and appropriately defensive (per-activation try/catch, sanitized logging), but there are two behavioral gaps worth the author's attention before merge, plus no test coverage for the new cascade.


Critical Issues (Must Fix)

No critical issues found.


Warnings (Should Fix)

  • XiansAi.Server.Src/Shared/Services/TenantService.cs:837 — The deactivation retry path loses its audit trail. agentSummary is computed whenever requestedEnabled == false (including retries where the tenant is already disabled), but it's only appended to the emitted domain event/audit description when enabledChanged is also true (line 837's if). Since retries by definition don't change Enabled, a retried disable that fails again (or succeeds) produces no event/webhook/audit record at all — the caller has no way to see the retry's outcome except server logs. Note: simply widening the condition to enabledChanged || !string.IsNullOrEmpty(agentSummary) is not a safe drop-in fix — the description text at line 858 assumes a state transition happened ("It was previously {(enabled ? "disabled" : "enabled")}"), so firing on a no-op retry would incorrectly claim the tenant "was previously enabled" when it was already disabled. This needs a distinct message/path for the retry case, which is an author judgment call.

  • XiansAi.Server.Src/Shared/Services/TenantService.cs:1132 — DeactivateActivationsForTenantAsync deactivates activations sequentially, in-band inside the UpdateTenant request handler (called synchronously from both TenantEndpoints.cs:76 and AdminTenantEndpoints.cs:411). Each iteration's DeactivateAgentAsync performs real Temporal calls (cancel/terminate workflows, delete schedules) via CleanupActivationResourcesAsync. For a tenant with many active activations, this can make the PUT/update-tenant request block for a long time and risk client/gateway timeouts, with no bound on the number of activations processed per request. Consider fire-and-forget/background processing (e.g. a queued job) or at least a cap with pagination/continuation, especially since the code already anticipates multi-call retries.

(No suggestion blocks included — both fixes require a design decision from the author rather than a drop-in code change.)


Suggestions (Consider Improving)

  • XiansAi.Server.Src/Shared/Services/TenantService.cs:1111 — No tests were added for DeactivateActivationsForTenantAsync/the new disable-cascade behavior (the only touched test file area, TenantServiceMetadataTests.cs, is unrelated — it covers metadata, not enable/disable). Given this changes production behavior for every tenant-disable call (cancelling live workflows), unit tests covering: normal deactivation, partial failure (some activations fail), full lookup failure, and the "already disabled, retry" path would materially reduce risk here.

Review Details

Code Quality

Clean, well-commented addition. The TenantAgentDeactivation record and its Describe() method give a nice compact summary type. Per-activation error handling (try/catch around each DeactivateAgentAsync call) correctly prevents one failure from blocking the rest of the batch.

Security

No security issues found. Tenant/activation ownership is still validated inside DeactivateAgentAsync (tenant-id equality check), all logged identifiers go through LogSanitizer.Sanitize, and no new untrusted input is introduced by this diff (only doc-string / description text changes on the endpoints side).

Test Coverage

No new or updated tests accompany this behavioral change. See Suggestions above.

Performance

See the sequential in-request deactivation loop warning above — the main performance concern in this diff.


Files Reviewed

File Lines Changed Risk Notes
XiansAi.Server.Src/Shared/Services/TenantService.cs +82/-1 🟡 Medium New cascade-deactivation logic; audit-gap and in-request performance concerns above
XiansAi.Server.Src/Features/WebApi/Endpoints/TenantEndpoints.cs +1/-1 🟢 Low Doc-string only
XiansAi.Server.Src/Features/AdminApi/Endpoints/AdminTenantEndpoints.cs +2/-0 🟢 Low Doc-string only

Comment thread XiansAi.Server.Src/Shared/Services/TenantService.cs
var deactivated = 0;
var failed = 0;

foreach (var activation in activations)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning: DeactivateActivationsForTenantAsync runs sequentially, in-band, inside the UpdateTenant request handler (called synchronously from both TenantEndpoints.cs and AdminTenantEndpoints.cs). Each iteration's DeactivateAgentAsync performs real Temporal calls (cancel/terminate workflows, delete schedules). For a tenant with many active activations this can make the update-tenant request block for a long time with no bound, risking client/gateway timeouts.

Fix: Consider moving this to background/async processing (e.g. a queued job) or bounding/paginating the batch so a single HTTP request can't be blocked on an unbounded number of Temporal calls.

Comment thread XiansAi.Server.Src/Shared/Services/TenantService.cs Outdated
@Diyanada99x
Diyanada99x marked this pull request as draft September 29, 2026 05:51
@Diyanada99x Diyanada99x self-assigned this Sep 30, 2026
Comment on lines +86 to +89
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error deactivating agents of disabled tenant {TenantId}", LogSanitizer.Sanitize(job.TenantId));
}
Comment on lines +96 to +98
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
}
@Diyanada99x Diyanada99x closed this Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-dlc/pr/pr-review Ready for AI-DLC PR review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deactivating a tenant should bring down all the agents into an deactivated state.

2 participants