File: shared_test.rb

package info (click to toggle)
ruby-with-advisory-lock 5.3.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 244 kB
  • sloc: ruby: 818; makefile: 14
file content (134 lines) | stat: -rw-r--r-- 2,920 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# frozen_string_literal: true

require 'test_helper'
class SharedTestWorker
  def initialize(shared)
    @shared = shared

    @locked = nil
    @cleanup = false
    @thread = Thread.new { work }
  end

  def locked?
    sleep 0.01 while @locked.nil? && @thread.alive?
    @locked
  end

  def cleanup!
    @cleanup = true
    @thread.join
    raise if @thread.status.nil?
  end

  private

  def work
    Tag.connection_pool.with_connection do
      Tag.with_advisory_lock('test', timeout_seconds: 0, shared: @shared) do
        @locked = true
        sleep 0.01 until @cleanup
      end
      @locked = false
      sleep 0.01 until @cleanup
    end
  end
end

class SharedLocksTest < GemTestCase
  def supported?
    %i[trilogy mysql2 jdbcmysql].exclude?(env_db)
  end

  test 'does not allow two exclusive locks' do
    one = SharedTestWorker.new(false)
    assert_predicate(one, :locked?)

    two = SharedTestWorker.new(false)
    refute(two.locked?)

    one.cleanup!
    two.cleanup!
  end
end

class NotSupportedEnvironmentTest < SharedLocksTest
  setup do
    skip if supported?
  end

  test 'raises an error when attempting to use a shared lock' do
    one = SharedTestWorker.new(true)
    assert_nil(one.locked?)

    exception = assert_raises(ArgumentError) do
      one.cleanup!
    end

    assert_match(/#{Regexp.escape('not supported')}/, exception.message)
  end
end

class SupportedEnvironmentTest < SharedLocksTest
  setup do
    skip unless supported?
  end

  test 'does allow two shared locks' do
    one = SharedTestWorker.new(true)
    assert_predicate(one, :locked?)

    two = SharedTestWorker.new(true)
    assert_predicate(two, :locked?)

    one.cleanup!
    two.cleanup!
  end

  test 'does not allow exclusive lock with shared lock' do
    one = SharedTestWorker.new(true)
    assert_predicate(one, :locked?)

    two = SharedTestWorker.new(false)
    refute(two.locked?)

    three = SharedTestWorker.new(true)
    assert_predicate(three, :locked?)

    one.cleanup!
    two.cleanup!
    three.cleanup!
  end

  test 'does not allow shared lock with exclusive lock' do
    one = SharedTestWorker.new(false)
    assert_predicate(one, :locked?)

    two = SharedTestWorker.new(true)
    refute(two.locked?)

    one.cleanup!
    two.cleanup!
  end

  class PostgreSQLTest < SupportedEnvironmentTest
    setup do
      skip unless env_db == :postgresql
    end

    def pg_lock_modes
      Tag.connection.select_values("SELECT mode FROM pg_locks WHERE locktype = 'advisory';")
    end

    test 'allows shared lock to be upgraded to an exclusive lock' do
      assert_empty(pg_lock_modes)
      Tag.with_advisory_lock 'test', shared: true do
        assert_equal(%w[ShareLock], pg_lock_modes)
        Tag.with_advisory_lock 'test', shared: false do
          assert_equal(%w[ShareLock ExclusiveLock], pg_lock_modes)
        end
      end
      assert_empty(pg_lock_modes)
    end
  end
end