Un singolo errore dovrebbe fallire un'operazione in blocco?


11

Nell'API su cui sto lavorando c'è un'operazione di eliminazione in blocco che accetta una matrice di ID:

["1000", ..., "2000"]

Ero libero di implementare l'operazione di eliminazione come avevo ritenuto opportuno, quindi ho deciso di rendere l'intera operazione transazionale: ovvero, se un singolo ID non è valido, l'intera richiesta non riesce. Chiamerò questa modalità rigorosa .

try{
savepoint = conn.setSavepoint();

for(id : IDs)
    if( !deleteItem(id) ){
        conn.rollback(savepoint);
        sendHttp400AndBeDoneWithIt();
        return;
    }

conn.commit();
}

L'alternativa (implementata altrove nella nostra suite di software) è fare ciò che possiamo nel back-end e segnalare guasti in un array. Quella parte del software gestisce meno richieste, quindi la risposta non finisce per essere un array gigantesco ... in teoria.


Un recente bug che si è verificato in un server povero di risorse mi ha fatto rivedere il codice, e ora sto mettendo in dubbio la mia decisione originale, ma questa volta sono motivato più dalle esigenze aziendali piuttosto che dalle migliori pratiche. Se, ad esempio, non riesco a soddisfare l'intera richiesta, l'utente dovrà riprovare, mentre se un numero di elementi viene eliminato, l'utente può completare l'azione e quindi chiedere a un amministratore di fare il resto (mentre lavoro per correggere il bug !). Questa sarebbe la modalità permissiva .

Ho provato a cercare online qualche consiglio in merito, ma sono venuto a mani vuote. Quindi vengo da te: cosa ci si aspetta di più da operazioni di massa di questo tipo? Dovrei attenermi a qualcosa di più rigoroso o dovrei essere più permissivo?


9
Dipende. Qual è il costo di avere qualcosa non cancellato quando dovrebbe essere? (Il costo viene definito come dati errati, mal di testa, comportamento indesiderato, il tempo impiegato da un amministratore per risolverlo, ecc.) È accettabile? Se riesci a convivere con le conseguenze del non fallire tutto, provaci. Se ciò causasse un problema eccessivo, non farlo. Conosci il tuo software e le conseguenze, quindi dovrai fare una chiamata di giudizio.
Becuzz,

1
@Becuzz Il costo sarebbe che l'utente notasse uno o due avanzi e aprisse un ticket al riguardo; la situazione attuale è "omg delete is broken". Fortunatamente l'utente è in fondo al corridoio, quindi questa volta non è un grosso problema. Il punto è che mi piace fare la cosa giusta ogni volta che è possibile, e con una base di codice di oltre 10 anni, Dio sa che alcune cose possono essere fatte correttamente
rath

Penso che questo dipenda anche dal fatto che tu voglia o meno la scalabilità. Se non hai intenzione di avere molti ID, non dovrebbe importare troppo. Se hai intenzione di avere un milione di ID, o meglio ancora, non sei assolutamente sicuro che non accadrà, potresti passare un'ora a cancellare gli ID solo per averlo completamente resettato a causa di 1 ID non valido.
imnota4,

1
@ imnota4 Un punto eccellente che non avevo considerato. L'interfaccia utente limita la richiesta a un massimo di circa 250, ma il back-end non ha restrizioni. Posso chiederti di ripubblicare il tuo commento come risposta?
rath,

1
La modalità permissiva inoltre semplifica il lavoro degli amministratori perché non è necessario riprodurre l'errore con tutta la pila di ID. Potrebbe anche essere utile informare nella risposta la causa di ciascun errore. Osservando la causa, potrebbe essere possibile per l'utente finale risolverlo senza biglietti "omg delete is broken".
Laiv,

Risposte:


9

Va bene fare una versione 'rigorosa' o 'bella' di un endpoint di eliminazione, ma è necessario dire chiaramente all'utente cosa è successo.

Stiamo eseguendo un'azione di eliminazione con questo endpoint. Probabile DELETE /resource/bulk/o qualcosa di simile. Non sono schizzinoso. Ciò che conta qui è che, indipendentemente dal fatto che tu decida di essere severo o gentile, devi riferire esattamente cosa è successo.

Ad esempio, un'API con cui ho lavorato aveva un DELETE /v1/student/endpoint che accettava ID bulk. Mandavamo regolarmente la richiesta durante il test, ottenevamo una 200risposta e assumevamo che tutto andasse bene, solo per scoprire in seguito che tutti gli utenti dell'elenco erano entrambi nel database ancora (impostati su inattivi) o non effettivamente cancellati a causa di un errore che abbiamo incasinato le chiamate future GET /v1/studentperché abbiamo recuperato i dati che non ci aspettavamo.

La soluzione a questo è arrivata in un aggiornamento successivo che ha aggiunto un corpo alla risposta con gli ID che non sono stati eliminati. Questa è - per quanto ne sappia - una sorta di best practice.

In conclusione, indipendentemente da ciò che fai, assicurati di fornire un modo per far sapere all'utente finale cosa sta succedendo e forse perché sta succedendo. IE, se abbiamo scelto un formato rigoroso, la risposta potrebbe essere 400 - DELETE failed on ID 1221 not found. Se avessimo scelto una versione "carina", potrebbe essere 207 - {message:"failed, some ids not deleted", failedids:{1221, 23432, 1224}}(scusa la mia scarsa formattazione json).

In bocca al lupo!


6
207 Multi-Statuspotrebbe essere appropriato per quella parziale risposta
all'errore

1
DOVE ANDIAMO! Non me lo ricordo proprio! Vado avanti e aggiornerò la risposta con quello, dal momento che è effettivamente allo standard.
Adam Wells,

2

Uno dovrebbe essere severo e permissivo.

Di solito, i carichi di carico sono suddivisi in 2 fasi:

  • Validazione
  • Caricamento in corso

Durante la fase di convalida, ogni record viene esaminato rigorosamente per assicurarsi che soddisfi i requisiti delle specifiche dei dati. Si possono facilmente ispezionare 10 di 1000 di record in pochi secondi. I record validi vengono inseriti in un nuovo file da caricare, quelli non validi vengono contrassegnati e rimossi e di solito vengono inseriti in un file separato (salta file). Viene quindi inviata una notifica sui record che non hanno superato la convalida, in modo che possano essere ispezionati e diagnosticati ai fini della risoluzione dei problemi.

Una volta convalidati, i dati vengono quindi caricati. Di solito viene caricato in batch se è abbastanza grande da evitare transazioni a lungo termine o se si verifica un errore, sarà più facile recuperarlo. Le dimensioni del batch dipendono dalla dimensione del set di dati. Se uno ha solo pochi record 1000, un batch sarebbe OK. Qui puoi essere un po 'permissivo con gli errori, ma potresti voler impostare una soglia batch non riuscita per interrompere l'intera operazione. Forse se i lotti [N] falliscono, si fermerebbe l'intera operazione (se il server era inattivo o qualcosa di simile). Di solito, non ci sono errori a questo punto perché i dati sono già stati convalidati, ma se ci sono stati problemi di ambiente o altro, ricaricare semplicemente i lotti che non hanno funzionato. Questo rende il recupero un po 'più semplice.


Non convalido gli ID rispetto ai valori DB, provo solo a eliminarli e vedo come va, o ci vorrebbe per sempre. Annullare dopo N fallimenti sembra un suggerimento molto ragionevole, +1
rath

2

Un singolo errore dovrebbe fallire un'operazione in blocco?

Non esiste una risposta canonica a questo. È necessario esaminare le esigenze e le conseguenze per l'utente e valutare i compromessi. L'OP ha fornito alcune delle informazioni richieste, ma ecco come procederei:

Domanda 1 : "Qual è la conseguenza per l'utente se una singola eliminazione fallisce?"

La risposta dovrebbe guidare il resto del comportamento progettuale / implementato.

Se, come affermato dall'OP, è semplicemente l'utente nota l'eccezione e apre un ticket di guasto, ma non è interessato (gli elementi non eliminati non influiscono sulle attività successive), allora andrei con permissivo con una notifica automatica a te.

Se le eliminazioni non riuscite devono essere risolte prima che l'utente possa procedere, è chiaramente preferibile il severo.

Dare all'utente l'opzione (ad esempio, essenzialmente un flag ignore-failures con il valore rigoroso o permissivo di default) può essere l'approccio più user friendly.

Domanda 2 : "Ci sarebbero problemi di coerenza / coerenza dei dati se le attività successive venissero eseguite con elementi non cancellati ancora nell'archivio dati?"

Ancora una volta, la risposta determinerebbe il miglior design / comportamento. Sì -> Rigoroso, No -> Permissivo, Forse -> Rigoroso o selezionato dall'utente (in particolare se l'utente può fare affidamento per determinare con precisione le conseguenze).


0

Penso che questo dipenda dal fatto che tu voglia o meno la scalabilità. Se non hai intenzione di avere molti ID, non dovrebbe importare troppo. Se hai intenzione di avere un milione di ID, o meglio ancora, non sei assolutamente sicuro che non accadrà, potresti passare un'ora a cancellare gli ID solo per averlo completamente resettato a causa di 1 ID non valido.


-1

Direi che un punto importante qui è cosa significa che una gran parte delle cose deve essere cancellata.

Questi ID sono in qualche modo logicamente correlati, o è solo una convenienza / prestazione - raggruppamento in batch di questi?

In caso di connessione, anche vagamente, in qualche modo, ci proverei strict. Se è solo una modalità batch (ad esempio, l'utente fa clic su "salva" per i suoi ultimi minuti di lavoro e solo allora viene trasmesso il batch), sceglierei la permissiveversione.

Come afferma l'altra risposta: in ogni caso, dire all'utente esattamente cosa è successo.

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.