-
Notifications
You must be signed in to change notification settings - Fork 35
Description
- Laravel Version: 6.18.2
- Nova Version: 2.12.0
- PHP Version: 7.3.14
- Database Driver & Version:
- Operating System and Version: n/a
- Browser type and version: all
- Reproduction Repository: n/a
Description:
Very short description
In general, the front end should not be using the Client Side time as a source of truth when attempting to determine if a request is valid when checked against a Model's updated_at time, instead Nova should consider a server generated time.
The Specifics
Some of our users on company issued PC's are not time-syncing against an NTP server (for network reasons, policy reasons, etc). Some of them have already experienced a clock drift of, for example, 5 seconds (meaning their computer clock is 5 seconds in the past - this was checked via https://time.is. (Clock drift is a naturally occurring thing, esp. on laptops that go to sleep/wakeup, etc, it is something we cannot completely stop from happening.)
For these users that are 5 seconds in the past, they can update a resource cleanly, but if they save via Save & Continue Editing the clock drift becomes an issue for their next save.
Since the next fetch happens immediately after save/update, the client generated _retrieved_at timestamp from their own machine is also 1 second behind the server generated updated_at time on the model.
(Client side time is generated at resources/js/views/Update.vue#L238)
This mismatch in time, when saved a second time causes a 409 since the ResourceUpdateController is checking _retrieved_at time against the model's updated_at time.
(Server checked in src/Http/Controllers/ResourceUpdateController.php#L57-L75)
Steps To Reproduce:
-
Use a nova instance somewhere on a server that has a well regulated clock (like in AWS).
-
On a client computer, disable time syncing and move the clock a few seconds into the past.
-
Update a resource by clicking "Save & Continue Editing", attempt to save again.
Proposed Solution
Let the server generate the time to populate the _retrieved_at, OR if the model is using timestamps, populate the _retrieved_at with the updated_at time and ensure the check is that the retrieved is >= the models updated time.
Debugging information
To catch the issue we put some logging into the application (in the nova middleware stack):
// log nova update requests, making note of clock drift
if ($request->routeIs('nova.*') && $request->isMethod('PUT')) {
$retrievedAt = $request->input('_retrieved_at');
logger()->warning('Nova PUT method', [
'request_url' => $request->url(),
'response_status_code' => $response->getStatusCode(),
'retrieved_at_actual' => $retrievedAt,
'retrieved_at_formatted' => Carbon::createFromTimestamp($retrievedAt)->__toString(),
'resource_updated_at' => $response->original['resource']['updated_at'] ?? 'n/a',
]);
}And this is what we saw (this particular log shows a 7 second drift on the client's clock). The thing to notice here is that the updated_at time in the top request is later than the retrieved_at time sent in the second request:
