stefankroes / ancestry

Organise ActiveRecord model into a tree structure
MIT License
3.72k stars 458 forks source link

find and fix corrupted children counts #584

Open acolchagoff opened 2 years ago

acolchagoff commented 2 years ago

We have a rather large database, and one of our clients wrote in about a bug that we determined to be a result of children_count being wrong for many many users ( we aren't really sure how many) Is there something I can do to regenerate all children counts in our database?

ptorrsmith commented 2 years ago

Something like this ? We didn't implement it as not yet had the need, so can't guarantee it's solid. (inspired by Ancestry::ClassMethods.rebuild_depth_cache!)

    # Rebuild children cache if it got corrupted or if counter caching was just turned on
    def rebuild_children_count!
      raise Ancestry::AncestryException.new(I18n.t("ancestry.cannot_rebuild_children_count")) unless respond_to? :counter_cache_column

      self.ancestry_base_class.transaction do
        unscoped_where do |scope|
          scope.find_each do |node|
            node.update_attribute counter_cache_column, node.children.count
          end
        end
      end
    end
kbrock commented 2 years ago

The best way to do this is probably via a single sql query

This is probably not valid sql, but will hopefully get you close:

Direct descendents:

UPDATE #{table_name}
SET child_count = (
  select count(*)
  from table_name children
  where children.ancestry =
    case when #{table_name}.ancestry is null then #{table_name}.id::varchar
    else #{table_name}.ancestry || '/' || #{table_name}.id::varchar
    end
)

Probably wouldn't be that hard to get into a method. String concatenation is mostly consistent across databases, mysql or sqlite may use + instead.

For all child decedents, we'd need to include both the ancestry || '/' || id and ancestry || '/' || id || '/%'

kbrock commented 1 year ago

@denodster I'm digging into what may be causing this.

So that I may mimic your environment as closely as possible:

Any anything else you can think of that may help reproduce would be great.

I'll also be adding:

@ptorrsmith If you could share similar information, that would be great, too. (so I can take that into account when looking into better support for dependant_count_cache.)

kbrock commented 1 year ago

@denodster would you possibly be running mysql? I noticed locally that the touch tests seem to behaving differently on mysql than postgres. Wonder if the counters have a similar problem

kbrock commented 1 year ago

please check out rebuild_depth_cache! and rebuild_counter_cache! / rebuild_counter_cache_sql!

ref: #654 and #663