-
Notifications
You must be signed in to change notification settings - Fork 6
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
Reduce the number of queries #32
Conversation
I can integrate this into my solution for the container gateway busy errors / race conditions. |
This implements a singleton database connection using dependency injection. It also aims to reduce the number of queries to a minimum, aiming for a better performance. Especially with concurrent updates in mind.
4f119fd
to
fb9cff8
Compare
end | ||
repositories = database[:repositories] | ||
|
||
entries = user_repo_maps['users'].flat_map do |user_repo_map| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if import can handle this flat map - you may need to run it per user
@@ -25,6 +25,12 @@ class Plugin < ::Proxy::Plugin | |||
validate :pulp_endpoint, url: true | |||
|
|||
rackup_path File.join(__dir__, 'container_gateway_http_config.ru') | |||
|
|||
def load_dependency_injection_wirings(container, settings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, this is a DSL, not a separate class:
def load_dependency_injection_wirings(container, settings) | |
load_dependency_injection_wirings do |container, settings| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very strange, for some reason, this dependency injecting causes container gateway endpoints to return 404s. Something is bugging Sinatra.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, it seems to be killing the entire container gateway plugin, after refreshing the feature has disappeared. Interesting ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disabling all modules in the group ['container_gateway'] due to a failure in one of them: undefined method 'sqlite_db_path' for #<Hash
, okay, getting closer ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah of course, Database was module instead of a class. Things got easier to debug after I found out more logs were available on the smart proxy in the UI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should also have then in the regular process. Perhaps you need to raise the log level to see them in the console
|
||
def migrate_db(db_connection, container_gateway_path) | ||
Sequel::Migrator.run(db_connection, "#{container_gateway_path}/smart_proxy_container_gateway/sequel_migrations") | ||
# TODO: create a background service that does this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can start a service within smart-proxy.
This is configured via: https://github.com/theforeman/smart-proxy/blob/e8fe59f83843ca619264e8d4757bc99fcde93fa9/modules/dhcp_isc/configuration_loader.rb#L28
Note you can use container.get_dependency(:database)
there to pass the DB connection.
It's then a regular class: https://github.com/theforeman/smart-proxy/blob/develop/modules/dhcp_common/free_ips.rb
As you scale up, this may work better.
@@ -4,11 +4,13 @@ | |||
require 'sequel' | |||
module Proxy | |||
module ContainerGateway | |||
extend Proxy::DHCP::DependencyInjection |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clearly this needs to be something else than DHCP, so pretty much a copy of https://github.com/theforeman/smart-proxy/blob/develop/modules/dhcp/dependency_injection.rb
extend ::Proxy::Util | ||
extend ::Proxy::Log | ||
|
||
inject_attr :database, :database |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This provides a property database
on an instance, but I see everything is static. It would be better to refactor the class so it can be instantiated and inserted via dependency injection.
Another note: database
is an instance of Proxy::ContainerGateway::Database
, so you may need to use database.connection
(and expose that) or change the dependency injection to provide a Sequel::Database
instance.
class << self | ||
Sequel.extension :migration, :core_extensions | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if this is a correct implementation. Perhaps it needs to be done before the connect
call. It also needs a attr_reader :connection
to expose the connection.
@ekohl to make sure I understand the thinking behind a single DB connection -- by doing so we lose the ability to do concurrent writes to SQLite (which could be enabled via WAL), but in our case that would solve the race condition problem. Even here at the end it mentions the best way to avoid race conditions is by running single threaded: https://sequel.jeremyevans.net/2010/03/09/pessimistic-locking.html Speaking of which, if we're using a single DB connection, would it also make sense to enable |
I just did a quick experiment with adding some locks the base code + switching to WAL mode -- the DB did lock up under stress still. Seems pessimistic locking + WAL mode counteract each other. I think the results are no surprise. I'll do some stress-testing next with the bulk inserts.
|
end | ||
conn | ||
end | ||
user = database[:users].find_or_create(name: username) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like find_or_create is expected to run on a model rather than a dataset.
|
||
repository_users = database[:repository_users] | ||
repository_users.delete | ||
repositories_users.import([:repository_id, :user_id], entries) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mental note: If ignoring the changes to use a single DB connection, issues can pop up still during concurrent smart proxy syncs:
2024-03-06T16:20:27 848a5696 [W] Error processing request '848a5696-6ca6-4138-898c-36377e3bfbea: <Sequel::UniqueConstraintViolation>: SQLite3::ConstraintException: UNIQUE constraint failed: repositories_users.repository_id, repositories_users.user_id
Also, I had to switch to importing DB IDs in entries
-- seems the Sequel Query there is disagreeing with import
. I wonder if it's because the query above repositories.where(name: user_repo_names, auth_required: true).select(:id, user_id: user.id)
needs a joins to repositories_users
.
So you notice it was all more of a general direction first pass attempt. Many details are wrong. Sorry about that, but I wanted to share before heading out for the day |
Totally understand, I was posting some comments to keep track of my thoughts. I'm pushing my continuation of this work here: #33 |
And rightfully so. As you could see, I did the same thing with annotating my own thoughts. |
Closing since f96702c is merged. |
No description provided.