synchronization - sirve - API REST de Pyramid: ¿Cómo manejo el acceso a datos concurrentes de forma segura?
servicios rest java (2)
Por lo general, se comienza por determinar qué tipo de modelo de consistencia es aceptable. Cuanto más débiles sean sus requisitos de consistencia, más fácil se volverá este problema en el lado del servidor.
Por ejemplo:
¿Es posible salirse con la concurrencia optimista? Supongamos que tiene el bloqueo, ejecute su operación, pero detecte cuándo hay una situación de concurrencia para que pueda recuperarse adecuadamente. Esta puede ser una buena opción si no espera mucha colisión. Sqlalchemy debería poder detectar que está actualizando una fila que ya ha sido modificada, por ejemplo.
Si eso no es aceptable, podría utilizar el bloqueo distribuido en redis. Probablemente podría usar esto para llegar a algún tipo de sincronización.
Estoy trabajando en una API REST para un servicio web utilizando Pyramid y Cornice ; los datos en el lado del servidor se manejan utilizando SQLAlchemy y MySQL . El servidor web es nginx utilizando uwsgi y está configurado para ejecutar varios procesos de Python:
[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
Problema
Supongamos una mesa de customers
en el lado del servidor. Al utilizar la API, puede leer los datos del cliente, modificarlos o eliminarlos. Además de eso, hay otras funciones de API que leen datos de clientes.
Podría emitir varias llamadas a la API al mismo tiempo, que luego compiten por el mismo recurso del cliente:
# 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
Esencialmente, este es un problema de lectores-escritores , pero como se trata de más de un proceso, la sincronización tradicional de subprocesos que usa locks/mutexes/semaphores no funcionará aquí.
Pregunta
Me gustaría entender la mejor manera de implementar el bloqueo y la sincronización para una API web basada en Pyramid, de manera que las llamadas concurrentes como en el ejemplo anterior se manejen de manera segura y eficiente (es decir, sin serialización innecesaria).
Soluciones (?)
- No creo que tenga sentido marcar / marcar al cliente
{id}
comolocked
porque SQLAlchemy guarda esas modificaciones, yflush()
no parece lo suficientemente atómico en este contexto. - Este artículo describe el uso de la ETag HTTP para administrar recursos compartidos.
- También se podría usar Redis como administrador de bloqueo distribuido para un spinlock para envolver una función de vista.
- ¿Qué pasa con el administrador de transacciones de Pyramid?
Supongo que está tratando con una base de datos MySQL y que sus bloqueos no necesitan cubrir otros recursos (Redis, API de terceros, etc.). También asumo que las funciones del lado del cliente no necesitan trabajar con los datos de las transacciones (mantener una sesión durante varias llamadas a la API), solo desea evitar el acceso simultáneo a la API para desordenar su base de datos.
Hay dos tipos de bloqueo, bloqueo pesimista y bloqueo optimista.
El bloqueo pesimista es lo que la mayoría de la gente suele saber al bloquear: usted crea y adquiere bloqueos de antemano, programados en código. Esto es lo que es el administrador de bloqueo distribuido.
El bloqueo optimista es lo que puede obtener fácilmente con las bases de datos SQL. Si dos transacciones compiten desde el mismo recurso, la base de datos condena efectivamente una de las transacciones y el marco de la aplicación (en este caso, Pyramid + pyramid_tm) puede volver a intentar la transacción N veces antes de abandonar.
El bloqueo optimista es una solución más ideal desde el punto de vista del desarrollo, ya que no pone ninguna carga cognitiva en el desarrollador de la aplicación para recordar bloquear los recursos correctamente o crear mecanismos de bloqueo internos. En su lugar, el desarrollador se basa en el marco y la base de datos para volver a intentar y gestionar situaciones de concurrencia. Sin embargo, el bloqueo optimista no es tan conocido entre los desarrolladores web, ya que hacer un bloqueo optimista en entornos populares en PHP es difícil debido a la falta de flexibilidad en el lenguaje de programación.
pyramid_tm
implementa una solución de bloqueo optimista y le recomendaría que lo use o alguna otra solución de bloqueo optimista, a menos que sepa una razón muy específica que no desea.
pyramid_tm
vincula el ciclo de vida de la transacción a la solicitud HTTP, muy natural desde el punto de vista del desarrollador webpyramid_tm
puede vincular otros eventos a transacciones exitosas, por ejemplo,pyramid_mailer
envía un correo electrónico a los usuarios solo si las transacciones se comprometenpyramid_tm
está bien probado y basado en el administrador de transacciones detransaction
ZODB, que ha estado en uso de producción desde principios de 2000Asegúrese de que su sesión de SQLAlchemy esté configurada en el nivel de aislamiento SERIALIZABLE de SQL ; comience con el modelo de mayor consistencia. Puede reducir este requisito de rendimiento si sabe que las llamadas de API lo toleran, por ejemplo, las llamadas que realizan análisis de solo lectura de estadísticas.
El bloqueo optimista por lo general se realiza mejor en muchas lecturas "normales" - pocas escrituras de cargas de trabajo donde es raro que surja un conflicto (dos llamadas a la API actualizan al mismo usuario una vez). La penalización de reintento de transacción solo se aplica si hay un conflicto.
Si la transacción finalmente falla después de los reintentos N, por ejemplo, en una situación de carga alta inusual, esto debe resolverse en el lado del consumidor de la API informando que los datos del lado del servidor han cambiado y el usuario debe verificar o rellenar el formulario
Otras lecturas