Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 14

Discussione: Overselling

  1. #1
    Utente di HTML.it L'avatar di boots
    Registrato dal
    Oct 2012
    Messaggi
    1,626

    Overselling

    Ciao a tutti,
    Vorrei realizzare un piccolo ecommerce per un sito, ma ho un dubbio su come gestire le quantità e di conseguenza evitare l'overselling.
    Ciò che vorrei fare è di non decrementare le quantità sul db fino alla fine del check-out, quindi a questo punto ricontrollare se tutti i prodotti nel carrello sono ancora disponibili, decrementare le quantità, salvare l'ordine nel db.

    Quindi, fondamentalmente

    Codice PHP:
    $cartIds = array(<id prodotti nel carrello>);
    $query "SELECT * FROM prodotti WHERE id IN $cartIds";
    $invalidProducts = array();
    foreach(
    $query as $prodottoo){
       if(
    $prodotto['quantita'] < $cart[$prodotto['id']]['qta'])
          
    $invalidProducts[] = $prodotto['id'];
    }

    if(empty(
    $invalidProducts)){
        foreach(
    $cart as $key => $prodotto){
              
    $query "UPDATE prodotti SET quantita = quantita - {$prodotto['qta']} WHERE id = $key";
        }
        
    $query "INSERT INTO ordini ....";
    }else{
        
    // Qualcuno ha già acquistato il prodotto e non ce ne sono più nella quantità richiesta

    Tralasciando la sintassi, secondo voi è possibile che un secondo script faccia la prima query (il controllo) mentre il primo ancora non finisce di aggiornare le quantità, portando ad un possibile overselling ?

    Qualche sugerimento?

    Grazie a tutti

  2. #2
    Utente di HTML.it L'avatar di Grino
    Registrato dal
    Oct 2004
    Messaggi
    739
    Se il motore DB è innoDB utilizza le transazioni. Se invece stai utilizzando come motore myisam utilizza l'istruzione sql lock table per bloccare le tabelle che devi aggiornare di modo che altri script non si sovrappongano.

    Siamo sempre troppo gelosi delle nostre grandi piccole opere! - Grino inedito.
    Lavori e Lavoretti

  3. #3
    Utente di HTML.it L'avatar di boots
    Registrato dal
    Oct 2012
    Messaggi
    1,626
    Alle transazioni ci avevo pensato...ma non riesco a capire se la tabella viene bloccata.
    Adesso faccio una prova...

    Edit: Si, le transazioni pare che vadano benissimo

  4. #4
    Le quantità vanno detratte in fase di checkout e, usando le innodb, puoi usare le transazioni ... come ti è stato già detto

    Ma attenzione, non devi fare una transazione per ogni singola detrazione ma piuttosto devi fare un'unica transazione, complessiva, dove effettui TUTTE le detrazioni e, alla fine, lanci una select per assicurarti che non ci siano quantità negative sui prodotti selezionati perché in caso invece di lanciare un commit lanci un rollback ed annulli le detrazioni e, nel contempo, avvisi l'utente

    Se vuoi stare più tranquillo, anche se è meno performante, puoi usare il locking delle tabelle, ovviamente non sei più legato alle inno db.
    Con il locking fai un lock PRIMA di iniziare le operazioni, poi lanci una select per controllare se ci sono le quantità e se tutto va bene fai le detrazioni e poi l'UNLOCK ... se mancano quantità avvisi l'utente e fai l'unlock.

    Con il lock non c'è possibilità che ti ritrovi con quantità negative perché non ci potranno essere più operazioni contemporanee, d'altro canto con le transazioni anche se devi fare più controlli non blocchi niente e quindi le performance sono superiori ed i tempi di esecuzioni minori.

    Ovviamente, con le innodb, devi accertarti che il tuo hoster te le fornisca, non tutti lo fanno

  5. #5
    Moderatore di PHP L'avatar di Alhazred
    Registrato dal
    Oct 2003
    Messaggi
    12,448
    La lock, la si può fare anche sulle tuple invece che sull'intera tabella, così da lasciare effettuare operazioni senza attesa a chi deve intervenire su altri prodotti.

    Per farlo basta effettuare una SELECT sui prodotti interessati, specificando FOR UPDATE nella query, in questo modo quei prodotti saranno disponibili solo all'interno della transazione che ha effettuato la SELSECT, tutti gli altri interessati ad uno o più di quei prodotti si metteranno in coda in attesa della chiusura della transazione.

  6. #6
    Beh ma così ti ritrovi nella situazione di dover controllare la disponibilità di ogni prodotto singolarmente (più query) e, in aggiunta, non essendo una transazione, se devi annullare tutto perché uno dei check è fallito devi fare tutti i vari update per ripristinare le disponibilità.
    A questo punto conviene più usare le transazioni, almeno il rollback è gestito automaticamente con un solo comando

  7. #7
    Moderatore di PHP L'avatar di Alhazred
    Registrato dal
    Oct 2003
    Messaggi
    12,448
    Dipende un po' da come uno vuole muoversi, ovviamente ogni metodo ha dei pro e dei contro.

    Facendo una lock sulla tabella si ha un accesso serializzato, quindi si mettono in coda tutti gli utenti e vengono serviti uno alla volta, se ce ne sono molti si rischiano lunghe attese o anche dei timeout del browser.
    Facendo una lock sulle tuple invece vengono accodati solo gli utenti che chiedono uno o più prodotti che già sono in elaborazione da un altro utente, gli altri proseguono senza attese.

    Ad ogni modo anche la SELECT FOR UPDATE si può inserire in una transazione, quindi il rollback lo si può lasciar eseguire a MySQL.

  8. #8
    Boh non saprei mai usate le select for update, ma le cose lunghe con il lock possono divenire un problema se si ci ritrova a gestire migliaia di uyenti

  9. #9
    Utente di HTML.it L'avatar di boots
    Registrato dal
    Oct 2012
    Messaggi
    1,626
    Grazie, per le risposte (pensavo che il 3d fosse caduto nel dimenticatoio )

    Controllando meglio, credo di avere qualche dubbio con le transazioni (InnoDB). Ho fatto un semplice script di test per vedere se le transazioni blocchino la tabella in lettura:

    test.php
    Codice PHP:
    <?php
    $db 
    mysql_connect('localhost''root''');
    mysql_select_db('test');
    mysql_query("START TRANSITION");
    $res mysql_query("SELECT * FROM test WHERE id=1");
    $data mysql_fetch_assoc($res);
    echo 
    $data['num'];
    if(!isset(
    $_GET['w']))
        
    sleep(8);
    mysql_query("UPDATE test SET test.num=9 WHERE id=1");
    mysql_query("COMMIT");
    ?>
    Inizialmente test.num = 8. dal browser ho lanciato test.php (pag1) e subito dopo test.php?w=1 (pag2)
    Quello che mi aspettavo e che pag2 attendesse la fine di pag1 e stampasse 9.
    Invece sia pag1 che pag2(che non attende) stampano 8....quindi le transazione non credo vadano bene, perchè io devo rendere atomiche la lettura e la scrittura

    Quindi credo che resti il lock. SELECT ... FOR UPDATE lo vorrei evitare perchè uso un framework (cakePHP) e non so se riesco ad usarla (vorrei usare i metodi del modello e non dover fare le query 'a mano')

    Cmq: secondo voi è giusto l'approccio di scalare le quantità dal db dopo il check-out, oppure considerare come acquistate anche quelle solo nel carrello ? (so che è quasi una domanda esistenziale , ma secondo voi cosa è meglio )

  10. #10
    Moderatore di PHP L'avatar di Alhazred
    Registrato dal
    Oct 2003
    Messaggi
    12,448
    Originariamente inviato da boots
    [...]
    Inizialmente test.num = 8. dal browser ho lanciato test.php (pag1) e subito dopo test.php?w=1 (pag2)
    Quello che mi aspettavo e che pag2 attendesse la fine di pag1 e stampasse 9.
    Invece sia pag1 che pag2(che non attende) stampano 8....quindi le transazione non credo vadano bene, perchè io devo rendere atomiche la lettura e la scrittura
    Il comportamento che ottieni è giusto rispetto al test.
    E' il test ad essere sbagliato perché tu fai così:
    - avvii lo script senza parametro w, il quale si limita a leggere il dato e stamparlo
    - avvii lo script con il parametro w, stavolta leggi il dato (che è ancora quello di prima) e lo stampi (quindi stampa la stessa cosa), poi aggiorni il valore del dato (solo adesso è cambiato)

    EDIT: mi sono appena accorto di essermi perso il ! nella lettura, quindi ti stampa sempre 8?

    Quindi credo che resti il lock. SELECT ... FOR UPDATE lo vorrei evitare perchè uso un framework (cakePHP) e non so se riesco ad usarla (vorrei usare i metodi del modello e non dover fare le query 'a mano')
    Sì, penso che non si possa fare usando gli strumenti che ti mette a disposizione CakePHP, io usando CodeIgniter le query con SELECT ... FOR UPDATE le devo fare a mano.
    Cmq: secondo voi è giusto l'approccio di scalare le quantità dal db dopo il check-out, oppure considerare come acquistate anche quelle solo nel carrello ? (so che è quasi una domanda esistenziale , ma secondo voi cosa è meglio )
    Il decremento va fatto assolutamente solo a fronte del checkout, altrimenti se ti capitano utenti cazzeggiatori che mettono oggetti nel carrello e poi non comprano, chi davvero vuole comprare si trova che non c'è disponibilità dei prodotti quando invece li potrebbe comprare.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.