Skip to content

Instantly share code, notes, and snippets.

@thibaudcolas
Created February 1, 2023 13:49
Show Gist options
  • Save thibaudcolas/9c8cd3fa9e0e482c7949914424dc582d to your computer and use it in GitHub Desktop.
Save thibaudcolas/9c8cd3fa9e0e482c7949914424dc582d to your computer and use it in GitHub Desktop.
Sage’s "Page locking spike" from Wagtail 4.2

Auto locking mechanism UX details

Auto lock vs manual lock

Based on the discussion in wagtail/wagtail#7636, we would like to have an automatic page locking mechanism to support autosave in the future. This automatic locking will be separate from (thus can co-exist with) the existing page locking feature, as it will have more advanced logic tightly coupled to the client-side interaction.

For clarity, we'll refer to the two locking mechanisms as follows:

  • "auto lock" - the new automatic locking mechanism that prevents two tabs from editing the same page at the same time
  • "manual lock" - the existing locking mechanism that allows a user to prevent other users from editing the page

In general, an auto lock should be automatically acquired when a user visits the page edit view, and released when the user leaves.

An auto lock requires the user to have edit permissions on the page. This means that an auto lock can only be acquired if the manual lock (if any) does not lock the page for the user. In other words, an auto lock can only be acquired if any of these is true:

  • there is no manual lock on the page
  • the user is part of a group that has a permission for the page's manual lock
  • the user is a superuser

Ensuring the latest version is shown to user who has the auto lock

To ensure that the user who acquires an auto lock has the latest version of the page in the editor, acquiring an auto lock must happen when the edit view is initially loaded. (An exception to this will be explained later.)

As a user can open multiple editor tabs of the same page at the same time, the lock must only be acquired via the first tab. All the other tabs must be locked to prevent discrepancies in the editor. Thus, a "user" in the case of auto locks is more like a "session".

Auto lock must lapse after a period of inactivity

If a session is not actively editing the page for a prolonged period of time (e.g. forget to close the tab, leave the editor open for hours, or lose the internet connection), the auto lock should be released and thus acquirable by other sessions. This ensures the page is not stuck in the auto lock limbo in such situations.

When an auto lock is released, all other sessions who are viewing the editor while the page was still locked will receive a notification that says the page is now available for editing. This notification should tell them to reload if they wish to edit, making it up for grabs. Only the first session that reloads the page and successfully acquires the lock will be able to edit the page.

If an inactive session comes back active and the auto lock wasn’t acquired by any other session during the inactivity, the lock can be re-acquired immediately. We can be sure this session still has the latest version, since we know that no other session has changed the page since this session was last on it.

Meanwhile, if another session acquired an auto lock after the first session's auto lock lapsed, the first session should be forced to reload the page and re-do the edits.

Implementation idea

  • Add new database fields to store the locking information
    • auto_lock_browser_id UUIDField
    • auto_lock_expires_at DateTimeField
    • Put in a mixin, e.g. AutoLockMixin?
  • When user visits edit view
    • Acquire the lock if it isn't currently locked by anyone else and the user is allowed to edit the page
  • While they edit
    • Ping back to the server periodically as long as the user is doing something. This will extend the auto lock expiry
    • If they lose connection and the pings don't get through, the lock may lapse and someone else might acquire the lock
    • If they come back, we can check if someone else has edited the page in the meantime (by checking if auto_lock_browser_id is the same) and re-acquire the lock if not
      • If the auto_lock_browser_id had changed, they will be forced to reload the page (show the notification)
  • If an admin requests to unlock the page
    • Change auto_lock_browser_id
    • When the existing user's browser sends ping back to the server, they will be notified that they can no longer edit the page and need to reload
      • Generally, this would only be done if they are AFK so wouldn't normally reload their browser, allowing a new user to edit the page
  • When they leave the page
    • Send a request to the server to remove the auto lock (by setting auto_lock_browser_id/auto_lock_expires_at to empty values) using a beforeunload handler
  • When they submit the page
    • Make sure the auto lock fields are cleared. (Might not be necessary if the beforeunload handler also fires? But might be better to clear it anyway just in case the handler didn't get through.)

(Thanks Karl!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment