Skip to content

Add reset when UnknownAttributeError is hit

charlie ablett requested to merge cablett-reload-on-unknown-attribute-error into master

What does this MR do and why?

Sometimes our codebase on prod is already loaded when the database migrations are run underneath, and the attributes method, when run against an existing record gets the latest DB colummn SQL query returns new columns that ActiveRecord doesn't know about.

It raises an ActiveModel::UnknownAttributeError

We have prior art and a feature flag to perform a reset on the columns when this occurs with StatementInvalid error, with a specific error message in !124009 (merged).

This MR expands that class to also accept ActiveRecord::UnknownAttributeError. It retains the same condition (feature flag, not within 10 minutes of a previous reset).

To reproduce

Here's how you can test it locally. Use two terminals, one with rails console and one with postgres.

Start with master branch and a DB that hasn't got the column. I've used MemberRole as an example but you can use any table or column, even made up nonsense column names. Go wild 😆

  1. Start rails c in one terminal and call MemberRole.new.attributes. You shouldn't see the new column in that list (it's pulling from the cache - no SQL call). You can also call MemberRole.column_names to get this list.

  2. MemberRole.last.attributes (or create one if there isn't one). I didn't have any on my local so I did

m = MemberRole.create(namespace: Namespace.first, description: "test", base_access_level: "guest")
  1. In a second terminal, open gdk psql console, add the column to your DB (not via your migration). This simulates the DB migration happening on a different environment (say canary).
ALTER TABLE "member_roles" ADD "admin_merge_request" boolean DEFAULT FALSE NOT NULL;
  1. Back in your console, call MemberRole.last.attributes. The new column is in the list now (because it made an SQL call). But if I call MemberRole.new.attributes or MemberRole.column_list, it's not there. That's because it's pulling from the ActiveRecord cache with an outdated column list.

  2. To simulate this error, do:

> MemberRole.new(MemberRole.last.attributes)
  MemberRole Load (6.5ms)  SELECT "member_roles".* FROM "member_roles" ORDER BY "member_roles"."id" DESC LIMIT 1 
ActiveModel::UnknownAttributeError: unknown attribute 'admin_merge_request' for MemberRole.

          raise UnknownAttributeError.new(self, k.to_s)
          ^^^^^
from /Users/charlie/.asdf/installs/ruby/3.1.4/lib/ruby/gems/3.1.0/gems/activemodel-7.0.6/lib/active_model/attribute_assignment.rb:51:in `_assign_attribute'
  1. don't forget to remove the column in your psql.
 ALTER TABLE "member_roles" DROP COLUMN "admin_merge_request";

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by charlie ablett

Merge request reports