Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

Cache not expiring when model has empty update in transaction

Abierto
#555 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
35/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Estancado
Stack tecnológico
ruby
Área
backend

Línea de trabajo

Comienza en el método _run_commit_callbacks de IdentityCache y sigue cómo se establece @transaction_changed_attributes y cómo ActiveRecord y ar_transaction_changes invocan after_commit para varias actualizaciones. Reproduce los tres ejemplos de transacciones y, después, verifica que la caché refleje el estado final confirmado del modelo sin provocar regresiones en la expiración normal de la caché.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

We've encountered a situation where the cache isn't expired if, within a transaction, a model has a "non-update" followed by an update. Here's the code that created a stale cache in our application:

def update_user(id, attributes) # hash
  ApplicationRecord.transaction do
    User.find(id).update(attributes.except(:email))
    update_sign_in_information(id, attributes[:email])
  end
end

def update_sign_in_information(user_id, email)
  user = User.find(user_id)
  user.update(email: email)
  # other stuff
end

If only the email attribute is passed in, this leads to a stale cache. Here are some more examples that demonstrate the bug:

class Tester
  def self.test
    reset
    ApplicationRecord.transaction do
      user = User.first
      user.update({})
      user.update(name: "test2")
    end
    puts "###### user name: #{User.fetch(User.first.id).name}" # test2, good

    reset
    ApplicationRecord.transaction do
      User.first.update({})
      User.first.update(name: "test2")
    end
    puts "###### user name: #{User.fetch(User.first.id).name}" # test1, stale

    reset
    ApplicationRecord.transaction do
      User.first.update(name: "test1")
      User.first.update(name: "test2")
    end
    puts "###### user name: #{User.fetch(User.first.id).name}" # test1, stale

  end

  def self.reset
    User.first.update(name: "test1")
    User.fetch(User.first.id) # fill the cache
  end
end

A similar "bug" exists in ActiveRecord:

class User < ApplicationRecord
  after_commit :print_name

  def print_name
    puts user.name
  end
end

ApplicationRecord.transaction do
  User.first.update(name: "test1")
  User.first.update(name: "test2")
end

# prints test1 after the whole transaction is committed

The bug goes pretty deep and seems to involve the activerecord and ar_transaction_changes gems. Essentially, the @transaction_changed_attributes variable that IdentityCache's _run_commit_callbacks method is checking is attached to the record, but the after_commit callback is only being called on the first instance even if the transaction contains multiple updates. Here's the monkey patch my team is currently considering adding to address the issue, but could lead to performance regressions:

def _run_commit_callbacks
  # if destroyed? || transaction_changed_attributes.present?
    expire_cache
  # end
  super
end
Lenguaje dominante
Ruby
Estrellas
2k
Forks
175
Métricas de merge de PR
Sin PR fusionados en 30 d

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de Shopify/identity_cache

Todos los issues de Shopify/identity_cache

Issues similares

Más issues de Ruby

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.