Skip to content

Client side clock drift causes 409 on Save after "Update and Continue Editing" #2458

@ralphschindler

Description

@ralphschindler
  • 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:

  1. Use a nova instance somewhere on a server that has a well regulated clock (like in AWS).

  2. On a client computer, disable time syncing and move the clock a few seconds into the past.

  3. 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:

2020-03-26-09-27-16

Similar issues:
#1082 & #1853

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions