5

Ho bisogno di incrementare atomicamente un contatore del modello e utilizzare il suo nuovo valore (elaborato da un lavoro Sidekiq).Ottieni il valore del contatore atomico (incremento) con Rails and Postgres

Al momento, io uso

Group.increment_counter :tasks_count, @task.id 

nel mio modello che incrementa atomicamente il contatore.

Ma Ho anche bisogno del suo nuovo valore per inviare una notifica, se il contatore ha per es. il valore 50. Qualche idea? Chiudendo il tavolo/la fila o c'è un modo più semplice?

Modifica/SOLUZIONE

Sulla base mu è troppo breve 's risposta e il metodo update_counters di Rails, ho implementato un metodo di istanza (testato con PostgreSQL).

def self.increment_counter_and_return_value(counter_name, id) 
    quoted_column = connection.quote_column_name(counter_name) 
    quoted_table = connection.quote_table_name(table_name) 
    quoted_primary_key = connection.quote_column_name(primary_key) 
    quoted_primary_key_value = connection.quote(id) 

    sql = "UPDATE #{quoted_table} SET #{quoted_column} = COALESCE(#{quoted_column}, 0) + 1 WHERE #{quoted_table}.#{quoted_primary_key} = #{quoted_primary_key_value} RETURNING #{quoted_column}" 
    connection.select_value(sql).to_i 
end 

utilizzarlo come:

Group.increment_counter_and_return_value(:tasks_count, @task.id) 

utilizza RETURNING per andare a prendere il nuovo valore all'interno della stessa query.

risposta

5

tuo Group.increment_counter chiamata invia SQL come questo per il database:

update groups 
set tasks_count = coalesce(tasks_counter, 0) + 1 
where id = X 

dove X è @task.id. Il modo SQL per ottenere il nuovo valore tasks_counter è quello di includere una clausola RETURNING:

update groups 
set tasks_count = coalesce(tasks_counter, 0) + 1 
where id = X 
returning tasks_count 

Non so di qualsiasi conveniente modo per ottenere che Railsy SQL al database però. Il solito approccio Rails sarebbe quello sia fare un po 'di blocco e ricaricare @task o saltare il blocco e sperare per il meglio:

Group.increment_counter :tasks_count, @task.id 
@task.reload 
# and now look at @task.tasks_count to get the new value 

È possibile utilizzare RITORNO come questo però:

new_count = Group.connection.execute(%Q{ 
    update groups 
    set tasks_count = coalesce(tasks_counter, 0) + 1 
    where id = #{Group.connection.quote(@task.id)} 
    returning tasks_count 
}).first['tasks_count'].to_i 

You' d probabilmente vuole nascondere quel casino dietro un metodo su Group in modo che si potrebbe dire cose come:

n = Group.increment_tasks_count_for(@task) 
# or 
n = @task.increment_tasks_count 
+0

Grazie per avermi a questo. Ho aggiornato la mia domanda con un metodo di istanza basato sulla tua risposta. – tbuehl

Problemi correlati