Fluke MET/TEAM — Cutover V1 → V2 (Runbook)¶
This runbook describes the production switch of the MET/TEAM synchronisation from calServer V1 (Yii) to calServer V2 (Laravel) for one installation.
Complements the feature overview in Fluke MET/TEAM and the
development concept docs/konzept-metteam-synchronisation-v2.md.
Ground rule: exactly one sync master¶
The V1 cron and the V2 scheduler both consume the MET/TEAM AuditLog2 table
destructively. Running both at once loses events or processes them twice
(double push, echo loops).
A shared property therefore decides which side is the active sync master:
| Property | Location | Values | Default |
|---|---|---|---|
sync_master |
settings/mssql/ (shared property table) |
v1, v2 |
v1 (empty/unknown = v1) |
Effect of the flag:
sync_master = v1(or empty): V1 synchronises. V2 stands down — pull and push passes skip with reasonnot_master, and the V2 outbox enqueues nothing.sync_master = v2: V2 synchronises. The V1 console commandssyncDataFromMSSQLandtransferToMSSQLstand down immediately (early guard return) before they touch MET/TEAM at all. The synchronous V1 in-request pushes (inventory / calibration / result saves, deletes, callsheet close, the copy-all button) stand down as well — a V1 UI that is still reachable after the cutover can no longer write to MET/TEAM as a second writer.
The v1 default is deliberate: existing systems run V1, and V2 must never
assume ownership without an explicit cutover. The worst case of a
misconfiguration is thus "V2 does not synchronise" (visible and diagnosable) —
never "both at once".
Check the prerequisites (before the cutover)¶
- V2 connection: in the admin UI (Admin → Extensions → Fluke MET/TEAM → Connection) run "Test connection". It must succeed.
- SQL Server driver:
pdo_sqlsrvmust be installed on the V2 server. Without the driver the connection test fails before MET/TEAM is even reached. - Provisioned interface: run Tools → Diagnostics and check that "Objects missing" reads 0. Since V1 and V2 use the same provisioned SP interface, no reinstall is needed in that case; if objects are missing, "Provision now" next to the finding creates them.
!!! danger "Do not rely on provisioning_required: false"
The status only compares the stored stamp. It stays put when someone
removes procedures or triggers in MET/TEAM — reported from the field:
stamp 10, and not one of the thirteen objects present in the database.
A cutover on that basis hands ownership to a side that immediately
runs into "Could not find stored procedure". Only the diagnostics
actually look.
4. Sync flags: the desired directions (sync_inventory_bidirectional,
sync_calibration_bidirectional, sync_results_bidirectional) are set in V2
identically to V1.
Read the current master at any time — in the UI under Admin → Extensions → Fluke MET/TEAM → Synchronisation → Sync ownership (cutover), or on the console:
Cutover procedure¶
-
Stop the V1 cron. Disable the Docker cron (
run_cron.sh/ thecron_jobentryrun_cron_every_5_seconds). Finish or stop any runningCopy_All_To_METTEAMjobs. -
Let the backlog drain. Make sure V1's
AuditLog2backlog is fully processed (or deliberately known) before V2 takes over. -
Migrate the password (plaintext → encrypted). First as a dry run:
The plaintext is kept — it is still needed for a possible rollback. (V2 reads the plaintext as a fallback anyway; the migration ensures V2 then owns its own encrypted copy.)
- Switch the sync master to V2. In the UI under Admin → Extensions → Fluke MET/TEAM → Synchronisation → Sync ownership (cutover) press "Hand over to V2"; the confirmation dialog lists any open prerequisites (missing connection, outdated provisioning, disabled synchronisation). Equivalently on the console:
From here the V1 sync commands stand down (guard return), and V2 releases pull/push.
!!! note "A warning, not a barrier"
Neither the UI nor the console command refuses the switch when a
prerequisite is missing. That is deliberate: a hard barrier would block
the way back to v1 in exactly the situation where you need it.
- Enable the V2 scheduler. Make sure the Laravel scheduler runs
(
php artisan schedule:workor the cron entry* * * * * php artisan schedule:run). Pull and push run through it at the configured interval; circuit breaker and locks prevent overlap.
Verification after the cutover¶
- Status endpoint
/api/v2/metteam/status:sync_master: "v2",is_sync_master: true,provisioning_required: false. - Sync log (
/api/v2/metteam/sync-logs, "Log" tab): fresh pull/push entries, no recurring errors. - Manual trial run (optional):
php artisan metteam:pullandphp artisan metteam:pushreport processed entries instead of askippedreason.
Rollback¶
As long as the plaintext password is still present, the way back is safe at any time — in the UI via "Hand back to V1", or:
The way back applies to the primary instance only: it is the one that shares the setting with V1. Further instances are V2-only — no V1 stands behind them to take over, so neither the UI nor the API offers handing back (the API answers 409).
Then re-enable the V1 cron. V2 immediately falls back to not_master and
enqueues nothing more; V1 takes over again.
Final cleanup (only after it has proven stable)¶
Once V2 runs stably for good and V1 is permanently decommissioned, the plaintext password can be removed (fixes weakness S1 from the concept):
No more rollback without re-entering the password
After the plaintext is emptied, a re-activated V1 sync can no longer connect to MET/TEAM. Only take this step once a return to V1 is ruled out.
Test concept (Phase 5)¶
The cutover mechanism is fully covered by automated tests (sqlite,
FakeMetteamGateway — no MSSQL required):
| Area | Test | File |
|---|---|---|
| Master default & switching | MetteamSettingsTest (sync_master_defaults_to_v1, …_handed_to_v2, …_handed_back_to_v1, unknown_…_treated_as_v1) |
tests/Unit/Services/MetteamSettingsTest.php |
| V2 stands down (pull/push) | test_it_skips_when_v2_is_not_the_sync_master |
tests/Unit/Services/MetteamPullServiceTest.php, …/MetteamPushServiceTest.php |
| No outbox enqueues | test_nothing_is_enqueued_when_v2_is_not_the_sync_master |
tests/Unit/Services/MetteamOutboxTest.php |
| Cutover command | MetteamSyncMasterCommandTest (set, reset, invalid, no-op, abort) |
tests/Feature/Console/MetteamSyncMasterCommandTest.php |
| Cutover endpoint | MetteamControllerTest (switch and back, invalid value, permission guard, and that the settings form can not flip ownership) |
tests/Feature/Api/V2/MetteamControllerTest.php |
| Cutover per instance | MetteamInstanceControllerTest (primary writes the shared property; a row instance cannot go to v1) |
tests/Feature/Api/V2/MetteamInstanceControllerTest.php |
| Handover warnings | metteamHandoverWarnings (missing connection, outdated provisioning, disabled sync) |
frontend-v2/utils/metteam.test.ts |
| Password migration | MetteamMigratePasswordCommandTest (migrate, idempotent, dry-run, overwrite, purge, abort) |
tests/Feature/Console/MetteamMigratePasswordCommandTest.php |
| Status output | test_status_reports_the_sync_master |
tests/Feature/Api/V2/MetteamControllerTest.php |
The V1 guard is implemented as an early return in
SyncDataFromMSSQLCommand::actionRun() and TransferToMSSQLCommand::actionRun()
(property read property()->get('settings','mssql','sync_master')); it only
triggers on === 'v2' and leaves existing installations (empty value) unchanged.