Skip to content

Commit

Permalink
Adding table locking. Fixes #594
Browse files Browse the repository at this point in the history
  • Loading branch information
jwoertink committed Dec 26, 2024
1 parent 55556c9 commit 238b8a4
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 1 deletion.
15 changes: 15 additions & 0 deletions src/avram/database.cr
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,21 @@ abstract class Avram::Database
end
end

# Creates a lock on the table in `mode`
# ```
# AppDatabase.with_lock_on(User, mode: :row_exclusive) do
# user = UserQuery.new.id(1).for_update.first
# SaveUser.update!(user, name: "New Name")
# end
# ```
def self.with_lock_on(model : Avram::Model.class, mode : Avram::TableLockMode, &)
exec("BEGIN")
exec("LOCK TABLE #{model.table_name} IN #{mode} MODE")
yield
ensure
exec("END")
end

# Methods without a block
{% for crystal_db_alias in [:exec, :scalar, :query, :query_all, :query_one, :query_one?] %}
# Same as crystal-db's `DB::QueryMethods#{{ crystal_db_alias.id }}` but with instrumentation
Expand Down
14 changes: 13 additions & 1 deletion src/avram/query_builder.cr
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class Avram::QueryBuilder
@prepared_statement_placeholder = 0
@distinct : Bool = false
@delete : Bool = false
@for_update : Bool = false

def initialize(@table)
end
Expand Down Expand Up @@ -116,7 +117,7 @@ class Avram::QueryBuilder
end

private def sql_condition_clauses
[joins_sql, wheres_sql, group_sql, order_sql, limit_sql, offset_sql]
[joins_sql, wheres_sql, group_sql, order_sql, limit_sql, offset_sql, locking_sql]
end

def delete : self
Expand Down Expand Up @@ -158,6 +159,11 @@ class Avram::QueryBuilder
self
end

def for_update : self
@for_update = true
self
end

def order_by(order : OrderByClause) : self
reset_order if ordered_randomly?
@orders << order
Expand Down Expand Up @@ -384,4 +390,10 @@ class Avram::QueryBuilder
private def delete_sql : String
"DELETE FROM #{table}"
end

private def locking_sql : String?
if @for_update
"FOR UPDATE"
end
end
end
4 changes: 4 additions & 0 deletions src/avram/queryable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ module Avram::Queryable(T)
clone.tap &.query.offset(amount)
end

def for_update : self
clone.tap &.query.for_update
end

def first? : T?
with_ordered_query
.limit(1)
Expand Down
16 changes: 16 additions & 0 deletions src/avram/table_lock_mode.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Avram
enum TableLockMode
ACCESS_SHARE
ROW_SHARE
ROW_EXCLUSIVE
SHARE_UPDATE_EXCLUSIVE
SHARE
SHARE_ROW_EXCLUSIVE
EXCLUSIVE
ACCESS_EXCLUSIVE

def to_s
member_name.to_s.gsub("_", " ")
end
end
end

0 comments on commit 238b8a4

Please sign in to comment.