PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Racconta qual è la differenza nell'indice univoco e validates_uniqueness_of

Ecco la differenza tra indice univoco e validates_uniqueness_of

Questa è una patch per consentire ad ActiveRecord di identificare gli errori generati dal db per violazioni di vincoli univoci. Ad esempio, fa il seguente lavoro senza dichiarare un validates_uniqueness_of:

create_table "users" do |t|
  t.string   "email",   null: false
end
add_index "users", ["email"], unique: true

class User < ActiveRecord::Base
end

User.create!(email: '[email protected]')
u = User.create(email: '[email protected]')
u.errors[:email]
=> "has already been taken"

I vantaggi sono velocità, facilità d'uso e completezza --

Velocità

Con questo approccio non è necessario eseguire una ricerca db per verificare l'unicità durante il salvataggio (che a volte può essere piuttosto lento quando l'indice viene perso -- https://rails.lighthouseapp.com/projects/8994/tickets/2503-validate.. . ). Se ti interessa davvero convalidare l'unicità, dovrai comunque utilizzare i vincoli del database in modo che il database convaliderà l'unicità indipendentemente da cosa e questo approccio rimuove una query aggiuntiva. Controllare l'indice due volte non è un problema per il DB (è memorizzato nella cache la seconda volta), ma salvare un viaggio di andata e ritorno del DB dall'applicazione è una grande vittoria.

Facilità d'uso

Dato che devi comunque avere vincoli db per la vera unicità, questo approccio consentirà che tutto avvenga automaticamente una volta che i vincoli db sono a posto. Puoi comunque utilizzare validates_uniqueness_of se lo desideri.

Completezza

validates_uniqueness_of è sempre stato un po' un hack:non è in grado di gestire correttamente le condizioni di gara e si traduce in eccezioni che devono essere gestite utilizzando una logica di gestione degli errori alquanto ridondante. (Vedi la sezione "Concorrenza e integrità" in http://api.rubyonrails .org/classes/ActiveRecord/Validations/ClassMe... )

convalida_unicità_di non è sufficiente a garantire l'unicità di un valore. La ragione di ciò è che nella produzione, più processi di lavoro possono causare condizioni di gara:

  1. Due richieste simultanee tentano di creare un utente con lo stesso nome (e vogliamo che i nomi utente siano univoci)

  2. Le richieste vengono accettate sul server da due processi di lavoro che ora le elaboreranno in parallelo

  3. Entrambe le richieste scansionano la tabella degli utenti e vedono che il nome è disponibile

  4. Entrambe le richieste superano la convalida e creano un utente con il nome apparentemente disponibile

Per una comprensione più chiara, controlla questo

Se crei un indice univoco per una colonna significa che hai la garanzia che la tabella non avrà più di una riga con lo stesso valore per quella colonna. Utilizzare solo validates_uniqueness_of validation nel tuo modello non è sufficiente per imporre l'unicità perché possono esserci utenti simultanei che cercano di creare gli stessi dati.

Immagina che due utenti provino a registrare un account con la stessa email in cui hai aggiunto validates_uniqueness_of :email nel tuo modello utente. Se hanno premuto il pulsante "Registrati" contemporaneamente, Rails cercherà nella tabella degli utenti quell'e-mail e risponderà che è tutto a posto e che va bene salvare il record nella tabella. Rails salverà quindi i due record nella tabella utente con la stessa email e ora hai un problema davvero di merda da affrontare.

Per evitare ciò è necessario creare anche un vincolo univoco a livello di database:

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :email
      ...
    end
    
    add_index :users, :email, unique: true
  end
end

Quindi, creando l'indice univoco index_users_on_email, ottieni due vantaggi molto interessanti. Integrità dei dati e buone prestazioni perché gli indici univoci tendono ad essere molto veloci.

Se inserisci unique:true nella tabella dei tuoi post per user_id, non consentirà di inserire record duplicati con lo stesso user_id.