/polls/voters.json restituisce utenti duplicati nelle richieste paginate

Quando si chiama l’endpoint /polls/voters.json, utilizzando la paginazione, abbiamo scoperto che la prima chiamata restituisce 25 utenti come previsto, ma la seconda pagina restituisce 26 utenti, uno dei quali è stato restituito nella prima chiamata. Questo è coerente ogni volta che ci sono più di 25 utenti.

Il problema è qui nel plugin poll per discourse:

https://github.com/discourse/poll/blob/main/plugins/poll/lib/poll.rb#L223

L’offset viene calcolato presumendo che postgres SQL sia esclusivo con un limite del rigo utilizzando BETWEEN, quando invece è inclusivo.

La prima query restituisce effettivamente 25, perché i righi calcolati iniziano effettivamente da 1, non da 0.

La mia correzione proposta è semplice come:

   params = {
      offset: offset + 1,
      offset_plus_limit: offset + limit,
      option_digest: opts[:option_id].presence,
    }

Oppure, una soluzione più elegante potrebbe essere quella di utilizzare LIMIT e OFFSET di postgres

    params = {
      limit: limit,
      offset: offset,
      option_digest: opts[:option_id].presence,
    }
          WHERE pv.poll_id IN (:poll_ids)
                /* where */
        ) v
        ORDER BY digest, CASE WHEN rank = 'Abstain' THEN 1 ELSE CAST(rank AS integer) END, username
        LIMIT :limit OFFSET :offset
      SQL
2 Mi Piace

Ottima osservazione, aggiungo un #pr-welcome nel caso qualcuno voglia provare a risolvere questo problema.

Dobbiamo assicurarci di non rompere le cose esistenti nei sondaggi nel caso in cui il front-end dipenda erroneamente da presupposti errati qui.

1 Mi Piace

Grazie per la rapida risposta!
Proverò a configurare un ambiente, non volevo fare un PR senza testare. Chiunque si senta libero di anticiparmi. Non sono affatto uno sviluppatore Ruby. Non so nemmeno come modificare le specifiche o i test.

Ironicamente, nell’interfaccia utente c’è del codice che scarica i risultati in un set per aggirare questo bug.

2 Mi Piace

Per chiunque altro stia visualizzando, abbiamo apportato una correzione simile all’interfaccia utente per il nostro software per lotterie qui.

Un brindisi a @lukecyca di VHS per aver identificato l’area problematica.

C’è anche un rapporto correlato sul caricamento di più votanti nei sondaggi di gennaio.

1 Mi Piace

Ottima osservazione.

È ancora più divertente di così. Ho inviato un paio di PR molto grandi tempo fa per modernizzare il front-end e aggiungere il voto a scelta classificata (Ranked Choice).

L’enorme scala di quei PR ha sicuramente contribuito a farmi trascurare questo problema e le correzioni al back-end non erano realmente in programma (anche se nel processo di realizzazione di quei PR ho effettivamente risolto molti problemi che non erano strettamente di loro competenza).

Ammetto di aver seguito quel precedente del front-end stabilito nel 2018 per il voto a scelta classificata senza pensarci troppo :sweat_smile:. (Che voi avete anche seguito, seppur consapevolmente :laughing:)

È incredibile come le soluzioni alternative possano persistere senza che il problema sottostante venga identificato.

3 Mi Piace

Ho pensato di provare a contribuire per la prima volta, ma sembra che potrebbe essere un po’ più complicato del previsto. :wink:

Ho un Draft PR attivo: FIX: do not return duplicates from /polls/voters.json by clechasseur · Pull Request #1 · clechasseur/discourse · GitHub

In questo PR, ho prima aggiunto dei test che riproducono il problema, poi ho applicato la semplice correzione di Rob e i test sono passati.

Ho anche provato la soluzione più elegante (quella che avrei preferito), ma sebbene prevenga i votanti duplicati, cambia anche quale votante viene restituito in quale pagina, inclusa la prima, il che potrebbe potenzialmente essere considerato un breaking change (a seconda di come il frontend lo gestisce - non ho ancora guardato).

Tuttavia, facendo un passo indietro, mi chiedo quale sia il vero significato del parametro limit quando si chiama questo endpoint: non limita realmente il numero di votanti totali restituiti, ma solo il numero di votanti restituiti per ogni opzione del sondaggio. Puoi vedere questo effetto nel test che ho aggiunto per il sondaggio a scelta multipla qui - la prima pagina è effettivamente limitata a 2 votanti per opzione, ma in totale vengono restituiti tre votanti diversi (sparsi tra le opzioni). Passando alla soluzione elegante (cioè usando LIMIT :limit OFFSET :offset), il limit viene applicato al numero totale di voti e non di votanti. Non sono sicuro al 100% che sia meglio o più intuitivo.

Comunque, sono nuovo in questo e potrei star pensando troppo. La soluzione semplice rimuove i votanti duplicati e non causa troppi problemi, quindi potrebbe essere la strada da percorrere. Aspetterò un feedback prima di inviare un PR al repository principale.

A parte questo, penso che ci sia un altro bug in questa parte del codice. La query per caricare i votanti è ordinata per digest, rank e username - ma quando ordina per rank, usa questa condizione:

CASE WHEN rank = 'Abstain' THEN 1 ELSE CAST(rank AS integer) END

Tuttavia, ’Abstain’ corrisponde effettivamente al rank 0, non a 1 - anche il rank 1 può essere restituito come ’1’. Questo potenzialmente rende l’ordinamento non deterministico tra le query, il che significa che a seconda del numero di votanti e del valore limit utilizzato, potrebbe essere possibile saltare dei votanti durante le query paginate. Nei miei nuovi test, ho dovuto ordinare i votanti restituiti per aggirare la natura non deterministica. (Poiché è non deterministico, presumo che non sia facile da riprodurre in un test, ma posso provarci…)

1 Mi Piace

Ho seguito il link sopra a un report precedente, che include un link a un report precedente precedente che include qualcosa che sembra puntare anche a questa possibilità:

Tuttavia, è passato molto tempo, quindi il codice potrebbe essere stato totalmente diverso all’epoca. (Ho provato a tornare indietro nel tempo con Git, ma la cronologia si è fermata nel 2021, quindi presumo che il codice si sia spostato a un certo punto.)

Dopo alcune sperimentazioni, non sono riuscito a creare un test che causasse problemi a causa dell’ordinamento non deterministico dei votanti restituiti. Penso che ciò sia dovuto al modo in cui viene creata la query e al modo in cui vengono creati i voti del sondaggio, ma non ne sono sicuro al 100%.

Ho creato una PR con il mio test e le correzioni che propongo: FIX: Evita di restituire votanti duplicati dall’endpoint /polls/voters.json di clechasseur · Pull Request #34433 · discourse/discourse

3 Mi Piace

Grazie mille per la correzione e per la pazienza durante la nostra revisione.

L’ho approvato e unito. :partying_face:

3 Mi Piace

Questo argomento è stato chiuso automaticamente dopo 3 giorni. Non sono più consentite nuove risposte.