Class: GCSLock::Mutex

Inherits:
Object
  • Object
show all
Defined in:
lib/gcslock/mutex.rb

Instance Method Summary collapse

Constructor Details

#initialize(bucket, object, client: nil, uuid: nil, min_backoff: nil, max_backoff: nil) ⇒ Mutex

Returns a new instance of Mutex.


9
10
11
12
13
14
15
16
17
# File 'lib/gcslock/mutex.rb', line 9

def initialize(bucket, object, client: nil, uuid: nil, min_backoff: nil, max_backoff: nil)
  @client = client || Google::Cloud::Storage.new
  @bucket = @client.bucket(bucket, skip_lookup: true)
  @object = @bucket.file(object, skip_lookup: true)

  @uuid = uuid || SecureRandom.uuid
  @min_backoff = min_backoff || 0.01
  @max_backoff = max_backoff || 5.0
end

Instance Method Details

#lock(timeout: nil) ⇒ Boolean

Attempts to grab the lock and waits if it isn't available.

Parameters:

  • timeout (Integer) (defaults to: nil)

    the duration to wait before cancelling the operation if the lock was not obtained (unlimited if nil).

Returns:

  • (Boolean)

    `true` if the lock was obtained.

Raises:


28
29
30
31
32
33
34
35
36
37
38
# File 'lib/gcslock/mutex.rb', line 28

def lock(timeout: nil)
  raise LockAlreadyOwnedError, "Mutex for #{@object.name} is already owned by this process" if owned?

  begin
    Utils.backoff(min_backoff: @min_backoff, max_backoff: @max_backoff, timeout: timeout) do
      try_lock
    end
  rescue LockTimeoutError
    raise LockTimeoutError, "Unable to get mutex for #{@object.name} before timeout"
  end
end

#locked?Boolean

Verifies if the lock is already taken.

Returns:

  • (Boolean)

    `true` if this lock is currently held.


43
44
45
46
47
48
# File 'lib/gcslock/mutex.rb', line 43

def locked?
  @object.reload!
  @object.exists?
rescue Google::Cloud::NotFoundError
  false
end

#owned?Boolean

Verifies if the lock is already owned by this instance.

Returns:

  • (Boolean)

    `true` if this lock is currently held by this instance.


53
54
55
# File 'lib/gcslock/mutex.rb', line 53

def owned?
  locked? && @object.size == @uuid.size && @object.download.read == @uuid
end

#synchronize(timeout: nil) ⇒ Object

Obtains a lock, runs the block, and releases the lock when the block completes.

Parameters:

  • timeout (Integer) (defaults to: nil)

    the duration to wait before cancelling the operation if the lock was not obtained (unlimited if nil).

Returns:

  • (Object)

    what the called block returned.

Raises:


66
67
68
69
70
71
72
73
74
75
# File 'lib/gcslock/mutex.rb', line 66

def synchronize(timeout: nil)
  lock(timeout: timeout)
  begin
    block = yield
  ensure
    unlock
  end

  block
end

#try_lockBoolean

Attempts to obtain the lock and returns immediately.

Returns:

  • (Boolean)

    `true` if the lock was granted.


80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/gcslock/mutex.rb', line 80

def try_lock
  @client.service.service.insert_object(
    @bucket.name,
    name: @object.name,
    if_generation_match: 0,
    upload_source: StringIO.new(@uuid),
  )

  true
rescue Google::Apis::ClientError => e
  raise unless e.status_code == 412 && e.message.start_with?('conditionNotMet:')

  false
end

#unlockObject

Releases the lock.

Returns:

  • nil

Raises:


100
101
102
103
104
105
# File 'lib/gcslock/mutex.rb', line 100

def unlock
  raise LockNotOwnedError, "Mutex for #{@object.name} is not owned by this process" unless owned?
  @object.delete

  nil
end

#unlock!Object

Releases the lock even if not owned by this instance.

Returns:

  • nil

Raises:


112
113
114
115
116
117
118
# File 'lib/gcslock/mutex.rb', line 112

def unlock!
  @object.delete

  nil
rescue Google::Cloud::NotFoundError => e
  raise LockNotFoundError, "Mutex for #{@object.name} not found"
end