A default change saved the new default outside any lock, then moved every organization of the type inside the request, from a list read after the save. At size the request ran out of time. A failure part way left the rest unmoved, with "Details are in the server logs." as the only answer and committing again as the only re-run (#187). Migration 00028 gives the default change work it can own and resume: - core.plan_act_reached_org_types records which types an act reaches. core.plan_act_owns_org_type states once that the newest act to reach a type owns it, so a newer act takes over what an older one has not moved. - core.plan_act_obligations holds one row per organization the act must move. core.plan_act_orgs_without_pool records an organization with no default pool, which is logged and counted, not owed. - core.plan_act_listing_frontier states once which organizations the listing has not reached: the writer, the listing-done read, the poller's read and the card's count all call it, and PostgreSQL inlines it into each. - Four enclosed writers list, settle, record a failure (failed after five attempts) and requeue. core.record_plan_act now writes the reached types and refuses a reorder detail that disagrees with the ladder. core.grants.act_id names the act a grant came from. The commit's first transaction runs on the runner under the exclusive rendezvous, inside a 5 s deadline. It locks the type and refuses a reserved type, a candidate that is already the default ("Already the default.") and a ladder with no rank 0. It then saves the default and records the act. With no disposition given, it rolls back when an organization holds the outgoing default, and the 422 preview is rebuilt after the rollback through the capped read. A listing then writes the obligations in batches of 500, in org type and organization id order. Each move runs in its own transaction under the shared rendezvous. It locks the pool, locks the obligation together with its type's owner, and reads the current default and its rank 0. It then migrates or grandfathers the organization, settles it as already at the default, or, under an act with no disposition, leaves it untouched with a Warn line. A failure is recorded on the obligation alone with its step, in a plain transaction that takes no pool row. The request tries up to 50 obligations within 2 s. Its lock and transaction timeouts are 500 ms and 2 s, and a move they cut records no attempt. It then starts PlanActDrainWorkflow for the rest; PollPlanActs starts a drain for any act still owing work. The toast counts what is not yet reached and what has no default pool, and leaves out any count it could not read. The org type's card shows each open act's lines: organizations not yet reached, and those not moved, each with its reason, with Retry. They are read in two statements in one read-only repeatable-read transaction, and "Pending plan changes could not be read. Reference: {request id}." replaces them when that read fails. Retry is an act of its own: it requeues the failed obligations on the types the act still owns, and a retried organization is recorded under the operator who pressed Retry. It answers a lock wait with the collision sentence and any other failure with "Failed to retry. Reference: {request id}.". Along the way: - The default change's sentences ending "Details are in the server logs.", in six places, now end "Reference: {request id}.", logged through the request's logger. - The select's label becomes "New default", so the form library's refusal reads "Choose a new default from the list." and its required message "Choose a new default.". The change's specs now carry that label for org-types and form-library. The design states the frontier function and that the request's cap counts obligations tried, not settled. docs/database-locks.md, docs/plan-management.md and the two docs quoting the label follow the code. Goldens: - Behaviour changes in contention, default-already-at, default-migrate, default-none-migrate, default-needs-disposition, default-begin-fails, default-initiate-fails, default-migrate-restore-fails, default-grandfather, default-change-twice, default-current-refused, default-save-fails, the four removal-against-default files and the four tier-change-during-conferral files. - The three default form goldens move with the label. routes.golden gains the Retry route's probes. - default-list-fails is retired: no listing runs outside the act any more. New: default-listing-fails, default-superseded, default-request-cap, default-untouched-outgoing, default-retry, default-retry-nothing, default-preview-capped and default-gate-true-preview-empty. - The other 40 plan-act goldens gain only the reached-type, without-pool and obligation sections and the grants' act column. The full suite passes.
59 lines
1.9 KiB
JSON
59 lines
1.9 KiB
JSON
{
|
|
"answer": [
|
|
"status 200",
|
|
"[role=\"alert\"]: Choose a new default from the list.",
|
|
"[role=\"alert\"]: Choose a new default from the list."
|
|
],
|
|
"checked": [],
|
|
"logs": [],
|
|
"statements": [
|
|
"begin isolation level repeatable read read only",
|
|
"commit"
|
|
],
|
|
"rows": {
|
|
"tiers": [
|
|
"ladder | rank | product",
|
|
"<ladder:Main> | 0 | <product:Basic>"
|
|
],
|
|
"defaults": [
|
|
"org_type | default_ladder",
|
|
"<orgtype:T> | <ladder:Main>"
|
|
],
|
|
"grants": [
|
|
"grant_id | org | product | grant_reason | granted_by | extends | description | status | act",
|
|
"<uuid:1> | <org:Holder> | <product:Basic> | default | none | none | | active | none"
|
|
],
|
|
"provisions": [
|
|
"provision | pool | grant_id | status",
|
|
"<uuid:2> | <pool:Holder> | <uuid:1> | active"
|
|
],
|
|
"positions": [
|
|
"provision | ladder | product | status | ended",
|
|
"<uuid:2> | <ladder:Main> | <product:Basic> | active | false"
|
|
],
|
|
"transitions": [
|
|
"pool | provision | ladder | from_rank | to_rank | transition_type | actor_type | actor | reason",
|
|
"<pool:Holder> | <uuid:2> | <ladder:Main> | - | 0 | initiate | system | none | test fixture"
|
|
],
|
|
"entitlements": [
|
|
"pool | resource_key | resource_limit",
|
|
"<pool:Holder> | plan_golden_seats | 1"
|
|
],
|
|
"acts": [
|
|
"act | cause | actor_type | actor | note | request_id"
|
|
],
|
|
"act_details": [
|
|
"act | cause | ladder | ladder_name | product | product_name | previous_product | previous_product_name | org_type | org_type_name | previous_ladder | previous_ladder_name | disposition | retried"
|
|
],
|
|
"reached_org_types": [
|
|
"act | org_type | org_type_name | last_org"
|
|
],
|
|
"orgs_without_pool": [
|
|
"act | org_type | org"
|
|
],
|
|
"obligations": [
|
|
"act | org_type | org | pool | status | outcome | attempts | error_step | superseded_by | requeued_by"
|
|
]
|
|
}
|
|
}
|