pg_advisory_lock 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f4f49429d7f217072b2927e3f33876f0841caf8d027725bd7981eed010e1a37
4
- data.tar.gz: 4bd6514b91d8dad257d575b7147a41f8faa5006dd398a7d98d340a2612c0a4f8
3
+ metadata.gz: 8a63a1943d6c02f12078939e1c72cad482972a9493ec408a0984308b329e269b
4
+ data.tar.gz: fcf0e101ff148fa73651759f0dbe4fe32cbaac1be1b5e9a13f26a7816c2d3a15
5
5
  SHA512:
6
- metadata.gz: 92a9005c04a0b898ec8489027bb065ecf713ec75cc83fe0e7d0c721740874463bd80ccad2919a5be780f7b0cc5d6626593b3e5b844e9eb76b1f33851b41a6f9a
7
- data.tar.gz: 8199402c1fd8ab0b8c72df92698f94912a81bc91a4af64ba2edd9385d68d9dcf3e0439ba72ce23424be5e4db3738eade36ebd2127ee6a2372539452180e5c065
6
+ metadata.gz: 12b30ae854f94924bbced7bd831f4bbe89e682a50a0d01c299b7c3071c79e04e1d8cadadc5017574138d328104dc118f35f0fcee4e69889ea3ebb01bb0b8c8a8
7
+ data.tar.gz: 33ce47ca69b19de4985d8bf9d7517fecb5d9074bf3fe5ce53518b2b880ae6a7e1110f607a2160a6e8b8ee3928938603c75d53e957ee3f8e7dca1f5961b38682b
@@ -0,0 +1,69 @@
1
+ name: Tests
2
+ on:
3
+ pull_request:
4
+ push:
5
+ branches:
6
+ - master
7
+
8
+ jobs:
9
+ rubocop:
10
+ runs-on: ubuntu-latest
11
+ name: Rubocop lint
12
+ env:
13
+ RAILS_VERSION: '~> 6.0'
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: 2.7
19
+ bundler-cache: true
20
+ - name: Run rubocop
21
+ run: |
22
+ gem install bundler
23
+ bundle install
24
+ bundle exec rake rubocop
25
+ test:
26
+ runs-on: ubuntu-latest
27
+ strategy:
28
+ matrix:
29
+ ruby: [ '2.7', '3.0', '3.1' ]
30
+ rails: [ '~> 6.0', '~> 7.0' ]
31
+ name: Tests with Ruby ${{ matrix.ruby }} Activerecord ${{ matrix.rails }}
32
+ services:
33
+ # Label used to access the service container
34
+ postgres:
35
+ # Docker Hub image
36
+ image: postgres
37
+ # Provide the password for postgres
38
+ env:
39
+ POSTGRES_DB: postgres_db
40
+ POSTGRES_PORT: 5432
41
+ POSTGRES_USER: postgres_user
42
+ POSTGRES_PASSWORD: postgres_password
43
+ ports:
44
+ - 5432:5432
45
+ # Set health checks to wait until postgres has started
46
+ options: >-
47
+ --health-cmd pg_isready
48
+ --health-interval 10s
49
+ --health-timeout 5s
50
+ --health-retries 5
51
+ env:
52
+ RAILS_VERSION: ${{ matrix.rails }}
53
+ POSTGRES_HOST: localhost
54
+ POSTGRES_DB: postgres_db
55
+ POSTGRES_PORT: 5432
56
+ POSTGRES_USER: postgres_user
57
+ POSTGRES_PASSWORD: postgres_password
58
+ steps:
59
+ - uses: actions/checkout@v2
60
+ - uses: ruby/setup-ruby@v1
61
+ with:
62
+ ruby-version: ${{ matrix.ruby }}
63
+ bundler-cache: true
64
+ - name: Run tests
65
+ run: |
66
+ gem install bundler
67
+ bundle install
68
+ cp -v spec/config/database.ci.yml spec/config/database.yml
69
+ bundle exec rake spec
data/.rubocop.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  AllCops:
2
2
  DisplayCopNames: true
3
- TargetRubyVersion: 2.5
3
+ TargetRubyVersion: 2.7
4
4
  Exclude:
5
5
  - vendor/**/*
6
6
  - tmp/**/*
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # PgAdvisoryLock
2
2
 
3
+ ![tests](https://github.com/didww/pg_advisory_lock/actions/workflows/tests.yml/badge.svg)
4
+ [![Gem Version](https://badge.fury.io/rb/pg_advisory_lock.svg)](https://badge.fury.io/rb/pg_advisory_lock)
5
+
6
+
3
7
  Postgresql Advisory Lock for ActiveRecord.
4
8
  Allows to use mutex in applications that uses same database.
5
9
 
@@ -42,7 +46,7 @@ PgAdvisoryLock::Base.select_values 'SELECT id from users WHERE parent_name = ?',
42
46
  ## Development
43
47
 
44
48
  After checking out the repo, run `bin/setup` to install dependencies.
45
- Create `spec/config/database.yml` (look at `spec/config/database.travis.yml` for example).
49
+ Create `spec/config/database.yml` (look at `spec/config/database.example.yml` for example).
46
50
  You need to create test database, so run `psql -c 'CREATE DATABASE pg_advisory_lock_test;'`.
47
51
  Then, run `rake spec` to run the tests.
48
52
  You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -23,6 +23,8 @@ module PgAdvisoryLock
23
23
  # @param name [Symbol, String]
24
24
  # @param value [Integer, Array<(Integer, Integer)>]
25
25
  def register_lock(name, value)
26
+ raise ArgumentError, 'value must be integer or array of integers' unless int_or_array_of_ints?(value)
27
+
26
28
  _lock_names[name.to_sym] = value
27
29
  end
28
30
 
@@ -61,6 +63,16 @@ module PgAdvisoryLock
61
63
  def try_lock(name, transaction: true, shared: false, id: nil, &block)
62
64
  new(name, transaction: transaction, shared: shared, id: id, wait: false).lock(&block)
63
65
  end
66
+
67
+ private
68
+
69
+ def int_or_array_of_ints?(value)
70
+ return true if value.is_a?(Integer)
71
+
72
+ return true if value.is_a?(Array) && value.all? { |i| i.is_a?(Integer) }
73
+
74
+ false
75
+ end
64
76
  end
65
77
 
66
78
  # @param name [Symbol, String] - lock name (will be transformed to number).
@@ -84,8 +96,8 @@ module PgAdvisoryLock
84
96
  # @return yield
85
97
  def lock(&block)
86
98
  with_logger do
87
- lock_number = name_to_number
88
- advisory_lock(lock_number, &block)
99
+ lock_args = build_lock_args
100
+ advisory_lock(lock_args, &block)
89
101
  end
90
102
  end
91
103
 
@@ -99,62 +111,68 @@ module PgAdvisoryLock
99
111
  logger.tagged(self.class.to_s, name.inspect) { yield }
100
112
  end
101
113
 
102
- def advisory_lock(lock_number, &block)
114
+ def advisory_lock(lock_args, &block)
103
115
  if transaction
104
- transaction_lock(lock_number, &block)
116
+ transaction_lock(lock_args, &block)
105
117
  else
106
- non_transaction_lock(lock_number, &block)
118
+ non_transaction_lock(lock_args, &block)
107
119
  end
108
120
  end
109
121
 
110
- def transaction_lock(lock_number)
122
+ def transaction_lock(lock_args)
111
123
  raise ArgumentError, 'block required when not within transaction' if !block_given? && !sql_caller_class.transaction_open?
112
124
 
113
- return perform_lock(lock_number) unless block_given?
125
+ return perform_lock(lock_args) unless block_given?
114
126
 
115
127
  sql_caller_class.transaction do
116
- perform_lock(lock_number)
128
+ perform_lock(lock_args)
117
129
  yield
118
130
  end
119
131
  end
120
132
 
121
- def non_transaction_lock(lock_number)
133
+ def non_transaction_lock(lock_args)
122
134
  raise ArgumentError, 'block required on transaction: false' unless block_given?
123
135
 
124
136
  begin
125
- perform_lock(lock_number)
137
+ perform_lock(lock_args)
126
138
  yield
127
139
  ensure
128
- perform_unlock(lock_number)
140
+ perform_unlock(lock_args)
129
141
  end
130
142
  end
131
143
 
132
- def perform_lock(lock_number)
144
+ def perform_lock(lock_args)
133
145
  function_name = "pg#{'_try' unless wait}_advisory#{'_xact' if transaction}_lock#{'_shared' if shared}"
134
146
 
135
147
  if wait
136
- sql_caller_class.execute("SELECT #{function_name}(#{lock_number})")
148
+ sql_caller_class.execute("SELECT #{function_name}(#{lock_args})")
137
149
  else
138
- result = sql_caller_class.select_value("SELECT #{function_name}(#{lock_number})")
139
- raise LockNotObtained, "#{self.class} can't obtain lock (#{name}, #{id})" unless result
150
+ result = sql_caller_class.select_value("SELECT #{function_name}(#{lock_args})")
151
+ raise LockNotObtained, "#{self.class} can't obtain lock (#{name}, #{id.inspect})" unless result
140
152
  end
141
153
  end
142
154
 
143
- def perform_unlock(lock_number)
144
- sql_caller_class.select_value("SELECT pg_advisory_unlock#{'_shared' if shared}(#{lock_number})")
155
+ def perform_unlock(lock_args)
156
+ sql_caller_class.select_value("SELECT pg_advisory_unlock#{'_shared' if shared}(#{lock_args})")
145
157
  end
146
158
 
147
159
  # Converts lock name to number, because pg advisory lock functions accept only bigint numbers.
148
160
  # @return [String] lock number or two numbers delimited by comma.
149
- def name_to_number
150
- lock_number = _lock_names.fetch(name) do
161
+ def build_lock_args
162
+ lock_args = _lock_names.fetch(name) do
151
163
  raise ArgumentError, "lock name #{name.inspect} is invalid, see #{self.class}::NAMES"
152
164
  end
153
- lock_number = Array.wrap(lock_number)
154
- lock_number.push(id) if id.present?
155
- raise ArgumentError, "can't use lock name #{name.inspect} with id" if lock_number.size > 2
165
+ lock_args = Array.wrap(lock_args)
166
+ if id.present?
167
+ if id.is_a?(Integer)
168
+ lock_args.push(id)
169
+ else
170
+ lock_args.push("hashtext(#{sql_caller_class.connection.quote(id.to_s)})")
171
+ end
172
+ end
173
+ raise ArgumentError, "can't use lock name #{name.inspect} with id" if lock_args.size > 2
156
174
 
157
- lock_number.join(', ')
175
+ lock_args.join(', ')
158
176
  end
159
177
 
160
178
  def sql_caller_class
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgAdvisoryLock
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
@@ -29,5 +29,5 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  spec.add_dependency 'activerecord'
31
31
  spec.add_dependency 'activesupport'
32
- spec.add_dependency 'pg_sql_caller'
32
+ spec.add_dependency 'pg_sql_caller', '>= 0.2.2'
33
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_advisory_lock
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Talakevich
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-13 00:00:00.000000000 Z
11
+ date: 2023-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: 0.2.2
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: 0.2.2
55
55
  description: Postgresql Advisory Lock for ActiveRecord.
56
56
  email:
57
57
  - senid231@gmail.com
@@ -59,10 +59,10 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
+ - ".github/workflows/tests.yml"
62
63
  - ".gitignore"
63
64
  - ".rspec"
64
65
  - ".rubocop.yml"
65
- - ".travis.yml"
66
66
  - CODE_OF_CONDUCT.md
67
67
  - Gemfile
68
68
  - LICENSE.txt
data/.travis.yml DELETED
@@ -1,6 +0,0 @@
1
- ---
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.5.5
6
- before_install: gem install bundler -v 2.1.4