Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audit to a second database #257

Closed
JamesChevalier opened this issue Feb 16, 2016 · 10 comments
Closed

Audit to a second database #257

JamesChevalier opened this issue Feb 16, 2016 · 10 comments

Comments

@JamesChevalier
Copy link

I'd like to send all of my audited data to an audits table that resides in another database. For regular Rails models, I'm able to add establish_connection "audit_database_#{Rails.env}".to_sym to the file & add a audit_database_production entry to the database.yml file.

Is this possible within audited?

@JamesChevalier
Copy link
Author

I think I've sorted this out in an initializer:

Audited::Adapters::ActiveRecord::Audit.class_eval do
  establish_connection "audit_#{Rails.env}".to_sym
end

I noticed that the master branch and the release branch are quite different from each other, so I don't know if this will continue to work after the master branch is released.

@mnoack
Copy link

mnoack commented Feb 17, 2016

FYI: We ended up making an 'auditor' app which stored all our audits (we had about 100 GB which is why it had to be stored elsewhere), in our main app we tweaked audited to send the changes this app, if that is something you would be interested in I'm sure we could open source it.

But it is currently running against audited for rails 3, so it probably needs a little work getting it compatible with rails 4/5.

@JamesChevalier
Copy link
Author

Thank you for offering to open source your solution! That's a much larger undertaking than is probably warranted to just help me along. If you feel that it would be useful to the audited community in general, then that would be nice - please don't go through all of that trouble just to help me out, though.

Thanks!

@kennethkalmer
Copy link
Collaborator

@JamesChevalier your solution of simply using Audit.establish_connection should be perfectly fine, as long as it is called on the ActiveRecord model we use for audits, regardless of master/release branch.

It must add a huge caveat though, and that the magical joins of ActiveRecord will blow up spectacularly if you try to do something like User.includes(:audits).find(1)... If you're having more troubles, please let me know.

@mnoack thanks indeed for the offer! Maybe in blog post form would be a great start rather than committing to a new project!

Closing for now, please reopen if needed!

@JamesChevalier
Copy link
Author

JamesChevalier commented Sep 21, 2016

I had an issue with audited v4.3.0 ... I still have a config/initializers/audited.rb file with this contents:

# Make Audited send its data to a different database
Audited::Adapters::ActiveRecord::Audit.class_eval do
  establish_connection "audit_#{Rails.env}".to_sym
end

I upgraded to version 4.3.0, then ran rails generate audited:install, and this error was returned:

/Users/me/Documents/code/my-app/config/initializers/audited.rb:2:in `<top (required)>': uninitialized constant Audited::Adapters (NameError)
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/engine.rb:652:in `block in load_config_initializer'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/activesupport-4.2.7.1/lib/active_support/notifications.rb:166:in `instrument'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/engine.rb:651:in `load_config_initializer'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/engine.rb:616:in `block (2 levels) in <class:Engine>'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/engine.rb:615:in `each'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/engine.rb:615:in `block in <class:Engine>'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/initializable.rb:30:in `instance_exec'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/initializable.rb:30:in `run'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/initializable.rb:55:in `block in run_initializers'
    from /Users/me/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/tsort.rb:228:in `block in tsort_each'
    from /Users/me/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
    from /Users/me/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/tsort.rb:422:in `block (2 levels) in each_strongly_connected_component_from'
    from /Users/me/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/tsort.rb:431:in `each_strongly_connected_component_from'
    from /Users/me/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/tsort.rb:421:in `block in each_strongly_connected_component_from'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/initializable.rb:44:in `each'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/initializable.rb:44:in `tsort_each_child'
    from /Users/me/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/tsort.rb:415:in `call'
    from /Users/me/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/tsort.rb:415:in `each_strongly_connected_component_from'
    from /Users/me/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/tsort.rb:349:in `block in each_strongly_connected_component'
    from /Users/me/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/tsort.rb:347:in `each'
    from /Users/me/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/tsort.rb:347:in `call'
    from /Users/me/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/tsort.rb:347:in `each_strongly_connected_component'
    from /Users/me/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/tsort.rb:226:in `tsort_each'
    from /Users/me/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/tsort.rb:205:in `tsort_each'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/initializable.rb:54:in `run_initializers'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/application.rb:352:in `initialize!'
    from /Users/me/Documents/code/my-app/config/environment.rb:5:in `<top (required)>'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/application.rb:328:in `require_environment!'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/commands/commands_tasks.rb:142:in `require_application_and_environment!'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/commands/commands_tasks.rb:128:in `generate_or_destroy'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/commands/commands_tasks.rb:50:in `generate'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
    from /Users/me/.rvm/gems/ruby-2.3.0@myapp/gems/railties-4.2.7.1/lib/rails/commands.rb:17:in `<top (required)>'
    from bin/rails:4:in `require'
    from bin/rails:4:in `<main>'

Based on this commit, it looks like we should just move the call out of the namespace. So, it would go from:

Audited::Adapters::ActiveRecord::Audit.class_eval do

to:

Audited::Audit.class_eval do

I made that change, and was able to verify that audits are produced when I change data.
So, this is my new config/initializers/audited.rb file:

# Make Audited send its data to a different database
Audited::Audit.class_eval do
  establish_connection "audit_#{Rails.env}".to_sym
end

@Marinofull
Copy link

Marinofull commented Apr 23, 2020

@JamesChevalier I am trying to follow this approach, and I wonder how the automation of the second database should work, also for rake db:migrate. Are you using another gem to manage the tasks across many dbs or what?

@Marinofull
Copy link

In my case, I'm using rails 4.2, which does not support multiple db natively, and the our first setup command rails generate audited:install fail, accusing the audit database is not configured

@Marinofull
Copy link

Here to make the rake db:migrate work, I ended changing the migration to:

def self.up
-    create_table :audits, force: true do |t|
+    Audited::Audit.connection.create_table :audits, force: true do |t|
      t.column :auditable_id, :integer
      t.column :auditable_type, :string
   ...

It's also required to create an entry in the config/database.yml for the "audit_#{Rails.env}" key suggested to use the the initializer

@fatfrog
Copy link

fatfrog commented Apr 27, 2022

@Marinofull is it possible to utilize Rails 6 multi-db setup? I have:

 class Audit < Audited::Audit
   self.abstract_class = true
   connects_to database: { writing: :audit, reading: :audit }
 end

but get the error:

NotImplementedError (OpsAudit is an abstract class and cannot be instantiated.)

@Marinofull
Copy link

@Marinofull is it possible to utilize Rails 6 multi-db setup? I have:

 class Audit < Audited::Audit
   self.abstract_class = true
   connects_to database: { writing: :audit, reading: :audit }
 end

but get the error:

NotImplementedError (OpsAudit is an abstract class and cannot be instantiated.)

I dont know, my application is now on 5.2, which has different dependencies

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants