REST è limitato al solo controllo ottimistico della concorrenza?


9

Contesto

A causa dell'apolidia dello stile architettonico REST che implica che ogni richiesta è completamente indipendente, il server non memorizza mai alcuna informazione sul client.

Pertanto, il controllo pessimistico della concorrenza non è adatto perché richiederebbe a quel server store quale client ottiene il blocco su una risorsa. Vengono quindi utilizzati controlli di concorrenza ottimistici, con l'aiuto Etagdell'intestazione. (a proposito, come ho chiesto lì /programming/30080634/concurrency-in-a-rest-api )

Problema

Il problema principale con un meccanismo di controllo della concorrenza ottimista è che si consente a tutti i tempi, tutti i client, di eseguire qualsiasi operazione.

E vorrei evitarlo senza infrangere il principio di apolidia di REST. Voglio dire che tutti i clienti non possono eseguire alcuna operazione in qualsiasi momento.

Domanda

Nella mia mente, sarebbe possibile con un semi-ottimista meccanismo di controllo della concorrenza, come quella:

  • I clienti possono richiedere un token
  • È possibile generare un solo token e ha un periodo di validità limitato
  • Per eseguire operazioni sulle risorse (come POST o PUT ), il client deve fornire questo token come parte del corpo (o intestazione?) Della richiesta. Il client che non ha il token non può eseguire queste operazioni.

È molto simile al controllo di concorrenza ottimista, tranne per il fatto che solo un client può eseguire alcune operazioni (quello che ha ottenuto il token) ... al contrario di "tutti i client possono eseguire tutte le operazioni".

Questo meccanismo è compatibile con uno stile architettonico REST? Si rompe qualcuno dei suoi vincoli? Stavo pensando di fare una domanda su SO, ma questa sembra più una domanda di alto livello, relativa alla progettazione del software.


1
In realtà è piuttosto semplice: devi modellare Transactionesplicitamente una risorsa.
Jörg W Mittag,

Cosa cambia? Non sono sicuro di capire il tuo commento. Non sto facendo il controllo della concorrenza per la transazione, ma piuttosto per dire al cliente "ok ora, sei assolutamente sicuro che nessuno cambierebbe la risorsa" (dato che ha un token univoco).
AilurusFulgens,

1
Qual è la differenza? Da un lato, si utilizza Etag per verificare, a chi è consentito aggiornare la versione "corrente"; se si scontra, devi aggiornare la tua versione e fare l'aggiornamento dopo. Dall'altro lato, hai il tuo meccanismo di "blocco": uno è permesso, altri devono aspettare. Sembra abbastanza lo stesso. L'aspetto negativo del secondo approccio: 2 richieste: 1 per il blocco e 1 per l'aggiornamento. In un caso ottimale, hai solo un aggiornamento tramite l'approccio Etag.
Thomas Junk,

Beh, in generale parlando del controllo della concorrenza ... il secondo che "perso la corsa" dovrà sempre aspettare (solo per motivi diversi, sono d'accordo con quello). Differenza con Etag? Con Etagte non sei mai sicuro che le tue operazioni saranno complete, potresti avere una situazione in cui non eseguirai mai alcuna operazione. Con un lucchetto, sei sicuro almeno di eseguire l'operazione. Quindi avere un semplice blocco è solo una via di mezzo tra un ambiente in cui possono verificarsi "conflitti elevati" e "conflitti rari".
AilurusFulgens,

Risposte:


3

Non dovresti mai (come mai e poi mai mai) bloccare alcuna risorsa mentre aspetti un'interazione dell'utente.

Ad un certo punto alcuni dei tuoi utenti decolleranno per un lungo weekend lasciando bloccati alcuni record vitali.

Ah, ma non lascerai che ciò accada perché hai qualche intelligente schema di risoluzione timeout / deadlock; poi ad un certo punto questo andrà terribilmente storto e un utente che ha ricevuto un bel messaggio "il tuo widget è stato ordinato" urlerà all'help desk chiedendo di sapere perché il suo widget non è stato consegnato.

Molte persone possono gestire il messaggio "mi spiace che un altro utente abbia appena ordinato questa parte".


L'hai spiegato bene. Anche se non sono completamente convinto della tua prima frase: a un certo punto garantiresti che nessun altro oltre a te possa eseguire un ordine. Ma bene, la tua ultima frase è un buon riassunto, nel 99% dei casi.
AilurusFulgens,

1
Se devi bloccare un record per un determinato utente, fallo a livello di applicazione. O sarà un semplice attributo "LockedBy" o, con alcuni più sofisticati meccanismi di controllo delle versioni e del flusso di lavoro.
James Anderson,

Cosa intendi con "a livello di applicazione"? Se aggiungi un attributo "LockedBy" alla tua risorsa, allora memorizzi le informazioni sul client sul tuo server. O forse non ho capito il tuo commento. Se puoi fornire alcuni dettagli, sarebbe fantastico!
AilurusFulgens,

1
@AilurusFulgens: aggiungi un attributo LockedBy (e possibilmente un timestamp LockedOn) al tuo database. Nel codice dell'applicazione imposti il ​​nome utente e l'ora ogni volta che il client avvia un'interazione di aggiornamento. Lo disinserisci quando il client è finito - devi quindi elaborare alcune regole aziendali per risolvere conflitti, timeout ecc.
James Anderson,

2

L'uso dei token è molto comune nelle API, questi token vengono generalmente inviati come intestazione e hanno un ciclo di vita chiaro. Pensa ad esempio a OAuth.

Indipendentemente dal linguaggio o dalla struttura di programmazione, le API REST sono simili.

Posso pensare a diversi scenari in cui si desidera limitare la concorrenza, due di questi sono:

  1. Più client aggiornano le stesse risorse come una riga del database. Ad esempio: due richieste simultanee, una elimina un record e l'altra tenta di aggiornare lo stesso record. A seconda del database e di come è stato impostato, è possibile ottenere un blocco sui record o un'operazione non valida poiché i dati saranno diversi o non esisteranno.

  2. Un superutente o amministratore che esegue azioni speciali con l'API.


Cosa fare in questi casi?

  1. Utilizzare transazioni nel database, singleton, blocchi e meccanismi simili per sincronizzare l'accesso alle risorse.

  2. Il token potrebbe funzionare, penso che sarebbe meglio se non memorizzi informazioni sul client, solo sul token stesso. In un solo passaggio è possibile convalidare l'utente e assegnare il token. Quindi convalidare che il token è attivo ed è valido. Utilizzare un token che può essere decrittografato per ottenere informazioni aggiuntive. È possibile memorizzare se si tratta di un token speciale e consentire solo alla volta. In questo modo si convalida il token, non l'utente.

Spero che questo possa essere d'aiuto.


Sì, mi aspettavo una risposta sulla somiglianza di un token e il meccanismo OAuth. Anche se l'ultimo è dedicato all'autenticazione. Non ho capito il punto del tuo scenario 1. La parte difficile da affrontare con la concorrenza in un'API REST è mantenere il vincolo di apolidia ... il che significa che il server non memorizza informazioni sul client. Il tuo scenario 2 è esattamente quello che sto facendo attualmente! :)
AilurusFulgens,

Ho risposto alla tua domanda?
Pablo Granados,

No scusa. Ho votato a favore della tua risposta perché offre una buona panoramica del problema, ma sfortunatamente, non lo sto davvero affrontando.
AilurusFulgens,

0

REST da solo è troppo primitivo, davvero. Puoi iniziare con REST, ma alla fine la tua ricca applicazione avrà bisogno di query con join e aggiornamenti con transazioni. Ogni sviluppatore che tenta di aggiungere queste cose da solo sarebbe soggetto a errori e incoerente. Fortunatamente, esiste uno standard emergente chiamato OData che fa proprio questo. Si sovrappone a REST e fornisce (1) linguaggio di query che consente di unire semplici utilizzando le proprietà di navigazione (senza dover esporre chiavi esterne) e, (2) elaborazione batch che include set di modifiche atomiche.

Vedere qui per (1) https://stackoverflow.com/a/3921423/471129 e,

Vedi qui e per (2) https://stackoverflow.com/a/21939972/471129

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.