¿REST solo se limita al control de concurrencia optimista?

9

Contexto

Debido a la apatridia del estilo arquitectónico REST que implica que cada solicitud se mantiene completamente sola, el servidor líder nunca almacena ninguna información sobre el cliente.

Por lo tanto, el control de concurrencia pesimista no es adecuado porque requeriría que el servidor almacene qué cliente obtiene el bloqueo en un recurso. Luego se utiliza el control de concurrencia optimista, con la ayuda del Etagencabezado. (por cierto, como pregunté allí /programming/30080634/concurrency-in-a-rest-api )

Problema

El principal problema con un mecanismo de control de concurrencia optimista es que permite que todo el tiempo, todos los clientes, realicen cualquier operación.

Y me gustaría evitar eso sin romper el principio de apatridia de REST. Quiero decir que todos los clientes no pueden realizar ninguna operación en ningún momento.

Pregunta

En mi opinión, sería posible con un mecanismo de control de concurrencia semi-optimista , así:

  • Los clientes pueden solicitar un token
  • Solo se puede generar un token y tiene un período de validez limitado
  • Para realizar operaciones en recursos (como POST o PUT ), el cliente debe proporcionar este token como parte del cuerpo (o encabezado?) De la solicitud. El cliente que no tiene el token no puede realizar estas operaciones.

Es muy similar al control de simultaneidad optimista, excepto que solo un cliente puede hacer algunas operaciones (el que obtuvo el token) ... al contrario de "todos los clientes pueden hacer todas las operaciones".

¿Este mecanismo es compatible con un estilo arquitectónico REST? ¿Rompe alguna de sus restricciones? Estaba pensando en preguntar sobre SO, pero esto parece más una pregunta de alto nivel, relacionada con el diseño de software.

AilurusFulgens
fuente
1
En realidad, es bastante simple: debe modelar Transactionexplícitamente como un Recurso.
Jörg W Mittag
¿Qué cambia? No estoy seguro de entender tu comentario. No estoy haciendo el control de concurrencia para la transacción, sino más bien para decirle al cliente "ok ahora, estás absolutamente seguro de que nadie cambiaría el recurso" (ya que obtuvo un token único).
AilurusFulgens
1
¿Cuál es la diferencia? Por un lado, utiliza el Etag para verificar quién puede actualizar la versión "actual"; si choca, debe actualizar su versión y hacer la actualización después de eso. Por otro lado, tiene su mecanismo de "bloqueo": uno está permitido, otros tienen que esperar. Suena bastante igual. La desventaja del segundo enfoque: 2 solicitudes: 1 para el bloqueo y 1 para la actualización. En un caso óptimo, solo tiene una actualización a través del enfoque Etag.
Thomas Junk
Bueno, en términos generales sobre el control de concurrencia ... el segundo que "perdió la carrera" siempre tendrá que esperar (solo por diferentes razones, estoy de acuerdo con eso). Diferencia con Etag? Con Etagusted nunca está seguro de que sus operaciones serán completas, usted podría tener una situación en la que nunca nunca va a realizar ninguna operación. Con un candado, al menos está seguro de realizar su operación. Por lo tanto, tener un bloqueo simple es solo un punto medio entre un entorno donde pueden ocurrir "conflictos altos" y "conflictos raros".
AilurusFulgens

Respuestas:

3

Nunca debe (como nunca nunca) bloquear ningún recurso mientras espera la interacción del usuario.

En algún momento, algunos de sus usuarios despegarán durante un largo fin de semana dejando algunos registros vitales bloqueados.

Ah, pero no dejarás que eso suceda porque tienes un esquema inteligente de tiempo muerto / resolución de punto muerto; luego, en algún momento, esto saldrá terriblemente mal y un usuario que recibió un mensaje agradable de "su widget ha sido ordenado" gritará en la mesa de ayuda exigiendo saber por qué no se entregó su widget.

La mayoría de las personas pueden lidiar con el mensaje "lo siento, otro usuario acaba de ordenar esta parte".

James Anderson
fuente
Lo explicaste muy bien. Aunque no estoy completamente convencido por su primera oración: en algún momento garantizará que nadie más que usted pueda realizar un pedido. Pero bueno, su última oración es un buen resumen, en el 99% de las veces es el caso.
AilurusFulgens
1
Si necesita bloquear un registro para un usuario en particular, hágalo a nivel de aplicación. O bien un simple atributo "LockedBy", o, con un mecanismo de flujo de trabajo y versiones más sofisticado.
James Anderson
¿Qué quiere decir con "a nivel de aplicación"? Si agrega un atributo "LockedBy" en su recurso, entonces almacena información sobre el cliente en su servidor. O tal vez no entendí tu comentario. ¡Si puede proporcionar algunos detalles, sería genial!
AilurusFulgens
1
@AilurusFulgens: agrega un atributo LockedBy (y posiblemente una marca de tiempo LockedOn) a su base de datos. En su código de aplicación, establece el nombre de usuario y la hora cada vez que el cliente inicia una interacción de actualización. Lo desarma cuando el cliente finaliza; luego, debe elaborar algunas reglas comerciales para resolver conflictos y tiempos de espera, etc.
James Anderson
2

El uso de tokens es muy común en las API, estos tokens generalmente se envían como un encabezado y tienen un ciclo de vida claro. Piense, por ejemplo, OAuth.

Independientemente de su lenguaje de programación o marco, las API REST son similares.

Puedo pensar en varios escenarios en los que desea limitar la concurrencia, dos de ellos son:

  1. Varios clientes actualizan los mismos recursos como una fila de base de datos. Por ejemplo: dos solicitudes simultáneas, una elimina un registro y la otra intenta actualizar el mismo registro. Dependiendo de su base de datos y de cómo lo haya configurado, es posible que obtenga un bloqueo en los registros o una operación no válida ya que los datos serán diferentes o no existirán.

  2. Un súper usuario o administrador que realiza acciones especiales con la API.


¿Qué hacer en estos casos?

  1. Use transacciones en la base de datos, singletons, bloqueos y mecanismos similares para sincronizar el acceso a los recursos.

  2. El token podría funcionar, creo que será mejor si no almacena información sobre el cliente, solo sobre el token en sí. En un paso, puede validar al usuario y asignar el token. Luego, solo valida que el token esté vivo y sea válido. Use un token que se pueda descifrar para obtener información adicional. Puede almacenar si este es un token especial y permitirlo solo a la vez. De esta manera, usted valida el token, no el usuario.

Espero que esto ayude.

Pablo Granados
fuente
Sí, esperaba una respuesta sobre la similitud de un token y el mecanismo OAuth. Aunque el último está dedicado a la autenticación. No entendí el punto de su escenario 1. La parte difícil de tratar con la concurrencia en una API REST es mantener la restricción de apatridia ... lo que significa que el servidor no almacena información sobre el cliente. ¡Tu escenario 2 es exactamente lo que estoy haciendo actualmente! :)
AilurusFulgens
¿Respondí tu pregunta?
Pablo Granados
No lo siento. Voté su respuesta porque brinda una buena visión general del problema, pero desafortunadamente, en realidad no lo está abordando.
AilurusFulgens
0

REST solo es demasiado primitivo, de verdad. Puede comenzar con REST, pero eventualmente, su rica aplicación necesitará consultas con combinaciones y actualizaciones con las transacciones. Cada desarrollador que intente agregar estas cosas por su cuenta sería propenso a errores e inconsistente. Afortunadamente, hay un estándar emergente llamado OData que hace exactamente eso. Se superpone a REST y proporciona (1) lenguaje de consulta que permite combinaciones simples utilizando propiedades de navegación (sin tener que exponer claves foráneas) y (2) procesamiento por lotes que incluye conjuntos de cambios atómicos.

Consulte aquí para (1) https://stackoverflow.com/a/3921423/471129 y,

Ver aquí y para (2) https://stackoverflow.com/a/21939972/471129

Erik Eidt
fuente