Skip to content

Commit bfa08b5

Browse files
committed
Backfill WAS_RUNNING events for running apps, tasks, and service instances
Seed a synthetic WAS_RUNNING usage event for every currently-running app process, a TASK_WAS_RUNNING event for every currently-running task, and a WAS_RUNNING event for every existing service instance, so billing consumers can bootstrap a complete baseline even after the original STARTED/TASK_STARTED/CREATED events have been pruned. The backfill is a batched, idempotent VCAP::WasRunningBackfill helper invoked from thin no_transaction migrations (mirrors the bigint-migration pattern): each batch keysets over the started processes / running tasks / service instances by id and runs in its own READ COMMITTED transaction, so no statement risks the migration timeout and MySQL's INSERT..SELECT takes no shared next-key locks on the scanned source rows while the API serves traffic. The app backfill scopes the package/droplet aggregates to each batch's apps (index-backed) to keep package_state fidelity without scanning the whole tables, and COALESCEs nullable legacy process/app/task columns so a single NULL row cannot abort a deploy. Because the API stays live during migrations, a batch can race a concurrent stop/delete and insert a baseline row that no later ending event would ever prune; a post-seed sweep removes WAS_RUNNING/TASK_WAS_RUNNING rows whose resource is no longer running/present. A skip_was_running_backfill config flag lets operators opt out (checked by the migrations, not the helper, since the migrations are recorded as applied either way); 'rake db:was_running_backfill' re-runs the seeding later for operators who skipped. Rollback deletes are batched too. Document the WAS_RUNNING/TASK_WAS_RUNNING states and their created_at (migration-time) semantics on the V3 resources, and list the new states in the legacy V2 usage-event docs because V2 reads the same event rows.
1 parent 8508a76 commit bfa08b5

19 files changed

Lines changed: 1041 additions & 2 deletions
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
require 'database/was_running_backfill'
2+
3+
Sequel.migration do
4+
no_transaction # backfill manages its own per-batch transactions
5+
6+
up do
7+
logger = Steno.logger('cc.backfill.was_running')
8+
if VCAP::WasRunningBackfill.skip?
9+
VCAP::WasRunningBackfill.log_skip(logger, 'app')
10+
else
11+
VCAP::WasRunningBackfill.seed_app_usage_events(self, logger)
12+
end
13+
end
14+
15+
down do
16+
VCAP::WasRunningBackfill.delete_app_usage_events(self)
17+
end
18+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
require 'database/was_running_backfill'
2+
3+
Sequel.migration do
4+
no_transaction # backfill manages its own per-batch transactions
5+
6+
up do
7+
logger = Steno.logger('cc.backfill.was_running')
8+
if VCAP::WasRunningBackfill.skip?
9+
VCAP::WasRunningBackfill.log_skip(logger, 'service')
10+
else
11+
VCAP::WasRunningBackfill.seed_service_usage_events(self, logger)
12+
end
13+
end
14+
15+
down do
16+
VCAP::WasRunningBackfill.delete_service_usage_events(self)
17+
end
18+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
require 'database/was_running_backfill'
2+
3+
Sequel.migration do
4+
no_transaction # backfill manages its own per-batch transactions
5+
6+
up do
7+
logger = Steno.logger('cc.backfill.was_running')
8+
if VCAP::WasRunningBackfill.skip?
9+
VCAP::WasRunningBackfill.log_skip(logger, 'task')
10+
else
11+
VCAP::WasRunningBackfill.seed_task_usage_events(self, logger)
12+
end
13+
end
14+
15+
down do
16+
VCAP::WasRunningBackfill.delete_task_usage_events(self)
17+
end
18+
end

docs/v2/app_usage_events/list_all_app_usage_events.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,9 +631,11 @@ <h4>Body</h4>
631631
<ul class="valid_values">
632632
<li>STARTED</li>
633633
<li>STOPPED</li>
634+
<li>WAS_RUNNING</li>
634635
<li>BUILDPACK_SET</li>
635636
<li>TASK_STARTED</li>
636637
<li>TASK_STOPPED</li>
638+
<li>TASK_WAS_RUNNING</li>
637639
</ul>
638640
</td>
639641
<td>

docs/v2/service_usage_events/list_service_usage_events.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ <h4>Body</h4>
290290
<li>CREATED</li>
291291
<li>DELETED</li>
292292
<li>UPDATED</li>
293+
<li>WAS_RUNNING</li>
293294
</ul>
294295
</td>
295296
<td>

docs/v3/source/includes/resources/app_usage_events/_object.md.erb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,14 @@ Name | Type | Description
3030
**instance_count.current** | _integer_ or `null` | Current instance count of the app that this event pertains to, if applicable
3131
**instance_count.previous** | _integer_ or `null` | Previous instance count of the app that this event pertains to, if applicable
3232
**links** | [_links object_](#links) | Links to related resources
33+
34+
#### WAS_RUNNING and TASK_WAS_RUNNING events
35+
36+
`WAS_RUNNING` and `TASK_WAS_RUNNING` are synthetic values for `state.current` recorded once per running process (`WAS_RUNNING`) and once per running task (`TASK_WAS_RUNNING`) by a one-time data migration when the keep-running cleanup feature was introduced. They mark every process and task that was already running at the time of the upgrade so that billing consumers can bootstrap from a complete baseline even if the original `STARTED`/`TASK_STARTED` events have been pruned.
37+
38+
**Consumer interpretation** (read `WAS_RUNNING`/`STARTED` as `TASK_WAS_RUNNING`/`TASK_STARTED` for task events, which are keyed by `task.guid`):
39+
40+
* If you have not previously recorded a `STARTED` event for this resource, treat `WAS_RUNNING` as equivalent to `STARTED`.
41+
* If you have already recorded `STARTED` (or an earlier `WAS_RUNNING`) for this resource, treat as a redundant baseline confirmation and ignore.
42+
* `created_at` reflects when the backfill migration ran, **not** when the app or task actually started. Treat `WAS_RUNNING` as a baseline marker that the resource was already running as of that timestamp, not as the true start of the running interval.
43+
* `state.previous` on a `WAS_RUNNING` event is always `null`. Subsequent real events for the same resource will continue to report their actual prior process state in `state.previous` (typically `STARTED`). If you perform chain validation, treat `WAS_RUNNING` as equivalent to `STARTED` for the purpose of validating the next event's `state.previous`.

docs/v3/source/includes/resources/service_usage_events/_object.md.erb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,13 @@ Name | Type | Description
2626
**service_broker.guid** | _string_ or `null` | Unique identifier of the service broker that this event pertains to, if applicable
2727
**service_broker.name** | _string_ or `null` | Name of the service broker that this event pertains to, if applicable
2828
**links** | [_links object_](#links) | Links to related resources
29+
30+
#### WAS_RUNNING events
31+
32+
`WAS_RUNNING` is a synthetic value for `state` recorded once per existing service instance by a one-time data migration when the keep-running cleanup feature was introduced. It marks every service instance that existed at the time of the upgrade so that billing consumers can bootstrap from a complete baseline of service instances even if the original `CREATED` events have been pruned.
33+
34+
**Consumer interpretation:**
35+
36+
* If you have not previously recorded a `CREATED` event for this service instance, treat `WAS_RUNNING` as equivalent to `CREATED`.
37+
* If you have already recorded `CREATED` (or an earlier `WAS_RUNNING`) for this instance, treat as a redundant baseline confirmation and ignore.
38+
* `created_at` reflects when the backfill migration ran, **not** when the service instance was created. Treat `WAS_RUNNING` as a baseline marker that the instance already existed as of that timestamp.

lib/cloud_controller/config_schemas/api_schema.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class ApiSchema < VCAP::Config
109109
optional(:migration_psql_concurrent_statement_timeout_in_seconds) => Integer,
110110
optional(:migration_psql_worker_memory_kb) => Integer,
111111
optional(:skip_bigint_id_migration) => bool,
112+
optional(:skip_was_running_backfill) => bool,
112113
db: {
113114
optional(:database) => Hash, # db connection hash for sequel
114115
max_connections: Integer, # max connections in the connection pool

lib/cloud_controller/config_schemas/migrate_schema.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class MigrateSchema < VCAP::Config
1010
optional(:migration_psql_concurrent_statement_timeout_in_seconds) => Integer,
1111
optional(:migration_psql_worker_memory_kb) => Integer,
1212
optional(:skip_bigint_id_migration) => bool,
13+
optional(:skip_was_running_backfill) => bool,
1314

1415
db: {
1516
optional(:database) => Hash, # db connection hash for sequel

0 commit comments

Comments
 (0)