I am working on a REST API for a web service using Pyramid and Cornice; data on the server side is handled using SQLAlchemy and MySQL. The web server is nginx using uwsgi, and it's configured to run multiple Python processes:
[uwsgi]
socket = localhost:6542
plugins = python34
...
processes = 2 # spawn the specified number of workers/processes
threads = 2 # run each worker in prethreaded mode with the specified number of threads
Problem
Suppose a table customers
on the server side. Using the API one can read customer data, modify it, or delete it. In addition to that there are other API functions which read customer data.
I could issue multiple API calls at the same time which then compete for the same customer resource:
# Write/modify the customer {id} data
curl --request POST ... https://some.host/api/customer/{id}
# Delete customer {id} and all of its associated data
curl --request DELETE https://some.host/api/customer/{id}
# Perform some function which reads customer {id}
curl --request GET ... https://some.host/api/do-work
Essentially this is a Readers-Writers Problem, but because more than one process is involved, traditional thread synchronization using locks/mutexes/semaphores won't work here.
Question
I'd like to understand the best way to implement locking and synchronization for such a Pyramid based web API, such that concurrent calls like in the above example are handled safely and efficiently (i.e. without unnecessary serializing).
Solutions (?)
- I don't think it makes sense to mark/flag customer
{id}
aslocked
because SQLAlchemy caches such modifications, andflush()
doesn't seem atomic enough in this context? - This article describes using the HTTP ETag to manage shared resources.
- One could also use Redis as a distributed lock manager for a spinlock to wrap a view function?
- What about Pyramid's transaction manager?