Make WordPress Core

Opened 6 years ago

Closed 5 years ago

#44886 closed defect (bug) (wontfix)

Race condition in REST API, can cause data loss

Reported by: rldk's profile rldk Owned by:
Milestone: Priority: normal
Severity: normal Version: 4.9.8
Component: Posts, Post Types Keywords:
Focuses: rest-api Cc:

Description

If, while a client is downloading a collection from the REST API, a
second client does something that should remove from that collection
a record which the first client has already downloaded, the REST API
will remove some *other* record from the first client’s collection in
order to make the total number of downloaded records be accurate.

For the demonstration below, the race condition is made repeatable by
having both clients pause at exactly the right (i.e. wrong!) moment.

Summary of the demonstration:

  1. The site holds “Important Post 1” through “Important Post 12”.
  2. Someone accidentally publishes “My test post”.
  3. An automated client begins downloading all posts (e.g. for backup, or syndication).
  4. Someone deletes “My test post”.
  5. The automated client finishes downloading.
  6. Later, when someone needs the downloaded dataset, “Important Post 3” is
    missing, just because someone deleted “My test post” during the download.

This bug was reported downstream at https://bugs.debian.org/898309.

To demonstrate:

(Files attached to this report are prefixed with “~/Downloads/” below.)

Installing and configuring the server software:

This isn’t meant to be secure; don’t expose it on a public IP address!

# cat /etc/issue.net
Debian GNU/Linux 9
# mkdir ~/wprestrace-server
# cd ~/wprestrace-server
# cp -a ~/Downloads/basic-auth.tar.gz .
# wget https://wordpress.org/wordpress-4.9.8.tar.gz
  [...]
# wget https://www.apachefriends.org/xampp-files/7.2.9/xampp-linux-x64-7.2.9-0-installer.run
  [...]
# chmod +x xampp-linux-x64-7.2.9-0-installer.run
# ./xampp-linux-x64-7.2.9-0-installer.run

(accept defaults, install, launch)
Manage Servers > MySQL Database > Start

# cd /opt/lampp/htdocs
# tar xpzf ~/wprestrace-server/wordpress-4.9.8.tar.gz
# mv wordpress wp
# tar xpzf ~/wprestrace-server/basic-auth.tar.gz -C wp/wp-content/plugins
# chown -R daemon:daemon wp
#

browse to http://localhost/phpmyadmin/
Databases
Database name: root_wp
Collation: utf8_unicode_ci
Create

browse to http://localhost/wp/
Continue
Let’s go!
Database Name: root_wp
Username: root
Password:
Submit
Run the installation
Site Title: tempsite
Username: username
Password: password
Install WordPress
Log In
Plugins > Installed Plugins > JSON Basic Authentication > Activate

Done installing and configuring the server software.

Installing and configuring the client software:

# mkdir ~/wprestrace-client
# cd ~/wprestrace-client
# cp -a ~/Downloads/WpRestRacePopulate.js .
# cp -a ~/Downloads/WpRestRaceDownloadAll.js .
# cp -a ~/Downloads/WpRestRaceTestPost.js .
# wget https://deb.nodesource.com/setup_8.x
  [...]
# chmod +x setup_8.x
# ./setup_8.x
  [...]
# apt-get install nodejs
  [...]
# node -v
v8.11.4
# npm install -g wpapi
+ wpapi@1.1.2
added 33 packages in 2.636s
#

Done installing and configuring the client software.

Reproducing the race condition:

# cd ~/wprestrace-client
# export NODE_PATH=$(npm root -g)
#
# node WpRestRacePopulate.js username password http://localhost/wp/wp-json
Created "Important Post 1".
Created "Important Post 2".
Created "Important Post 3".
Created "Important Post 4".
Created "Important Post 5".
Created "Important Post 6".
Created "Important Post 7".
Created "Important Post 8".
Created "Important Post 9".
Created "Important Post 10".
Created "Important Post 11".
Created "Important Post 12".
#

Open another terminal emulator.
When asked to press Enter below, don’t do it yet!

# cd ~/wprestrace-client
# export NODE_PATH=$(npm root -g)
#
# node WpRestRaceTestPost.js username password http://localhost/wp/wp-json
Created "My test post".  Press Enter to delete it.

Go back to the original terminal emulator.
When asked to press Enter below, don’t do it yet!

# node WpRestRaceDownloadAll.js username password http://localhost/wp/wp-json
Downloading all posts:
    Downloaded "My test post".
    Downloaded "Important Post 12".
    Downloaded "Important Post 11".
    Downloaded "Important Post 10".
    Downloaded "Important Post 9".
    Downloaded "Important Post 8".
    Downloaded "Important Post 7".
    Downloaded "Important Post 6".
    Downloaded "Important Post 5".
    Downloaded "Important Post 4".
Press Enter to continue.

Go back to the second terminal emulator, and press Enter.
The command and output (including what you already saw) are:

# node WpRestRaceTestPost.js username password http://localhost/wp/wp-json
Created "My test post".  Press Enter to delete it.
Deleted "My test post".
#

Go back to the original terminal emulator, and press Enter.
The command and output (including what you already saw) are:

# node WpRestRaceDownloadAll.js username password http://localhost/wp/wp-json
Downloading all posts:
    Downloaded "My test post".
    Downloaded "Important Post 12".
    Downloaded "Important Post 11".
    Downloaded "Important Post 10".
    Downloaded "Important Post 9".
    Downloaded "Important Post 8".
    Downloaded "Important Post 7".
    Downloaded "Important Post 6".
    Downloaded "Important Post 5".
    Downloaded "Important Post 4".
Press Enter to continue.
    Downloaded "Important Post 2".
    Downloaded "Important Post 1".
    Downloaded "Hello world!".
Finished downloading all posts:
    My test post
    Important Post 12
    Important Post 11
    Important Post 10
    Important Post 9
    Important Post 8
    Important Post 7
    Important Post 6
    Important Post 5
    Important Post 4
    Important Post 2
    Important Post 1
    Hello world!
End of listing.
#

The DownloadAll client thinks that it successfully downloaded all posts,
but it never received “Important Post 3”!

Attachments (4)

basic-auth.tar.gz (1.5 KB) - added by rldk 6 years ago.
WpRestRacePopulate.js (507 bytes) - added by rldk 6 years ago.
WpRestRaceDownloadAll.js (916 bytes) - added by rldk 6 years ago.
WpRestRaceTestPost.js (638 bytes) - added by rldk 6 years ago.

Download all attachments as: .zip

Change History (8)

@rldk
6 years ago

#1 @TimothyBlynJacobs
6 years ago

  • Severity changed from major to normal

#2 @adamsilverstein
6 years ago

Related, possible duplicate of #44568

This ticket was mentioned in Slack in #core-restapi by timothybjacobs. View the logs.


5 years ago

#4 @kadamwhite
5 years ago

  • Component changed from REST API to Posts, Post Types
  • Milestone Awaiting Review deleted
  • Resolution set to wontfix
  • Status changed from new to closed

This was discussed in the REST API weekly meeting today, and we've established that this behavior isn't limited to the API; it may be observed if using normal archive views within a traditional WordPress site as well.

The converse of the issue is that you might see a post on two subsequent archive pages if additional content is published after you load the first page, but before the second:

  • View page one, with posts 12 through 3
  • New post is published
  • Click "next page" and see posts 3 through 1

Because this is not specific to the REST API and appears to instead be a systemic issue with WordPress as a whole (hence the re-categorization as Posts/Post-Types), I'm going to close the ticket. In doing so I acknowledge that it's possible for this behavior to cause problems, but I don't see a path forward towards fixing it without fundamental architectural changes.

Note: See TracTickets for help on using tickets.