fix(ui): set active server at mount only (race-free)
Revert the hydrate-on-every-request sync — writing the single global active-server session on every async request (slow load(), polls, parallel tabs) is inherently last-writer-wins and races. Setting it only at mount (once per deliberate navigation) is race-free and fixes the visible drift. bfcache restore + multi-tab remain inherent edges of the single-session model — documented in the handoff. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>feat/v1-foundation
parent
7ea789c078
commit
0b1b5357d2
|
|
@ -61,29 +61,17 @@ class Show extends Component
|
||||||
*/
|
*/
|
||||||
public function mount(): void
|
public function mount(): void
|
||||||
{
|
{
|
||||||
$this->syncActiveServer();
|
// Viewing a server's details makes it the ACTIVE server, so the sidebar switcher
|
||||||
|
// (and the scoped sections) reflect the server on screen. Set ONLY at mount — i.e.
|
||||||
|
// once per deliberate navigation — so a slow in-flight load() or a parallel tab
|
||||||
|
// can never write a stale value over a newer selection (the single-session active
|
||||||
|
// server is last-writer-wins; we keep writes to deliberate navigations only).
|
||||||
|
session(['active_server_id' => $this->server->id]);
|
||||||
|
|
||||||
// identity + gauges render from the persisted row immediately;
|
// identity + gauges render from the persisted row immediately;
|
||||||
// the live snapshot loads lazily via wire:init -> load().
|
// the live snapshot loads lazily via wire:init -> load().
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Re-assert this server as the ACTIVE one on EVERY request to this component (mount,
|
|
||||||
* the wire:poll tick, any action). This is what keeps the sidebar switcher + scoped
|
|
||||||
* sections in sync with the server on screen — and it also covers a wire:navigate
|
|
||||||
* bfcache restore (Back/Forward), which skips mount() but still hits the server on
|
|
||||||
* the next poll/interaction. Server-side + idempotent, so there is no request race.
|
|
||||||
*/
|
|
||||||
public function hydrate(): void
|
|
||||||
{
|
|
||||||
$this->syncActiveServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function syncActiveServer(): void
|
|
||||||
{
|
|
||||||
session(['active_server_id' => $this->server->id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Lazy: pull the full SSH snapshot after the shell renders, behind skeletons. */
|
/** Lazy: pull the full SSH snapshot after the shell renders, behind skeletons. */
|
||||||
public function load(FleetService $fleet): void
|
public function load(FleetService $fleet): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -29,22 +29,6 @@ class ServerContextSyncTest extends TestCase
|
||||||
$this->assertSame($b->id, session('active_server_id'));
|
$this->assertSame($b->id, session('active_server_id'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_interacting_with_a_details_page_resyncs_active_server(): void
|
|
||||||
{
|
|
||||||
// Simulates a bfcache restore where mount() did not run and the session points
|
|
||||||
// elsewhere: the next request to the component (poll/action → hydrate) re-syncs.
|
|
||||||
$this->actingAs(User::factory()->create());
|
|
||||||
$a = $this->server('A');
|
|
||||||
$b = $this->server('B');
|
|
||||||
|
|
||||||
$component = Livewire::test(Show::class, ['server' => $a]);
|
|
||||||
session(['active_server_id' => $b->id]); // drift (as after Back/Forward)
|
|
||||||
|
|
||||||
$component->call('pollMetrics');
|
|
||||||
|
|
||||||
$this->assertSame($a->id, session('active_server_id'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_switching_on_a_details_page_targets_the_new_server(): void
|
public function test_switching_on_a_details_page_targets_the_new_server(): void
|
||||||
{
|
{
|
||||||
$a = $this->server('A');
|
$a = $this->server('A');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue