-
Notifications
You must be signed in to change notification settings - Fork 64
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
Support locking tables #594
Comments
Hey @huuhait! Right now Avram doesn't have any built-in support for locking tables directly. However, you can always drop down to raw SQL to do things like this. I haven't used locks, but I believe they only persist during a transaction, right? In which case, you can use: AppDatabase.transaction do
AppDatabase.run do |db|
# db is an instance of the crystal-db https://github.com/crystal-lang/crystal-db
# which will just proxy directly to the pg shard https://github.com/will/crystal-pg
db.query "CUSTOM SQL"
end
end Then if your lock needs to do a select on a specific model, you can build the query like normal sql = <<-SQL
BEGIN WORK;
LOCK TABLE #{table_for(User)} IN SHARE MODE;
#{UserQuery.new.admin(true).to_prepared_sql};
INSERT INTO admin_users VALUES (_id_);
COMMIT WORK;
SQL
db.query(sql) Here's some more guides on using the raw SQL options with Avram. https://luckyframework.org/guides/database/raw-sql If you get this working all buttery smooth, it would be great if you could open an issue on the website with how you did this. Then we could add some documentation showing how to handle it for others! |
is Avram will support locking table in futures? |
We probably can. I haven't seen anyone request that. I've worked on some really large apps, and have never needed to use table locking, so I'm not really sure how it all works. If we can figure out a clean type-safe way to add it in, then I think it would be a welcomed change! |
I'll leave the issue open. If you or anyone else comes across this and has a really good understanding of how to use them, please add some notes in here and we can work out how to best integrate this in to Avram. |
i'm building a cryptocurrency exchange |
That's awesome @huuhait! Definitely another area I don't know a single thing about. Once you have it working, any tips you can share here on how you did it would be awesome. |
what do you need me to provide information? about how it work |
Rails active_record supports two types of locking: |
I did have a need for table or column locking today. I am creating records with a unique key constraint. I check for existence of the key with a query, and then if it doesn't already exist, I create it. But there's a race: another instance can create the key in the interval between when I check for its existence and then create it. And then creating the record raises |
@BrucePerens does that happen when using the class SaveUser < User::SaveOperation
lock_table
end
SaveUser.upsert(...) Would this be a viable solution, or does it not make sense? This is where it gets foggy for me. I'd really love to see how others are using this in their apps to better determine what we would need to do to make it native. |
The table in question is for my url_path object, which is the fast resolution from a URL path to one of my other records. If I use upsert to create a new record which holds a reference to a new url_path, and there is a collision, we are left with something inconsistent. Probably two records that both hold references to a url_path that should have only one owner. Most likely this is a database consistency error because the reference should be unique. I could upsert both objects, but what I really want to do is detect the conflict and see which has better data before replacing one with the other. |
I managed to get table locking working within the context of a
Then the Ideally I could do something like So the long story short is that it works, but just not as idiomatically as you'd like. Maybe in the future a new hook ( |
Michael's method is interesting, but I think I need to lock the table at transaction begin, not the beginning of the save. The usual operation would be to read from the table, write something based on what I have read, commit (with implied unlock). I'm unfamiliar with the necessary SQL, I'll have to try it sometime. |
I don't know if this actually works, but here is my table locker: class AppDatabase
# Lock one or more tables. Creates a transaction, executes the block within the
# transaction with the given tables locked.
#
# Unlocking will happen when the *outermost* transaction commits.
#
# #Arguments#
# * *mode*: The locking mode to use, as an SQL fragment in a `String`. The default
# is `"EXCLUSIVE"`.
# * The other arguments are symbols or strings containing the names of the tables
# to lock.
def self.lock_table(*positional_arguments, mode : String = "EXCLUSIVE")
tables = String.build do |s|
first = true
positional_arguments.each do |a|
s << ", " if !first
s << a.to_s
first = false
end
end
self.transaction do
self.exec "LOCK TABLE #{tables} IN #{mode} MODE;"
yield
end
end
end |
Here is how you select with row locking FOR UPDATE in avram: sql = <<-END
SELECT * from product_categories
WHERE site_id = $1 AND name = $2
FOR UPDATE OF product_categories;
END
node = AppDatabase.query_all(sql, as: ProductCategory, args:[ctx.site.id, "Goods"]).first? Postgres seems to do some locking without being told, as part of regular operations, look at https://www.postgresql.org/docs/9.1/explicit-locking.html for a hint. |
Hi how i can lock table?
The text was updated successfully, but these errors were encountered: