What should be done if two workers access the same data simultaneously?
sobes.tech AI
Answer from AI
It is necessary to ensure thread-safe access to data. Main approaches:
-
Locks: Used to restrict access to critical sections of code. Only one thread can hold the lock at a time.
# Example with Mutex in Ruby require 'thread' $shared_data = 0 $mutex = Mutex.new def increment_data $mutex.lock # Acquire lock begin # Critical section temp = $shared_data sleep(0.01) # Simulate work $shared_data = temp + 1 ensure $mutex.unlock # Release lock end end threads = [] 10.times do threads << Thread.new { increment_data } end threads.each(&:join) puts $shared_data # Will guaranteed be 10 -
Database transactions: If data is stored in a database, use transactions for atomic operations. The transaction isolation level determines how changes made by other transactions are visible.
# Example in Rails using ActiveRecord ActiveRecord::Base.transaction do # All operations inside the block are atomic user = User.find(user_id) user.balance -= amount order = Order.create!(user: user, amount: amount) user.save! # If something goes wrong, the entire transaction is rolled back end -
Atomic operations: Some languages and libraries provide atomic data types or operations that are guaranteed to execute entirely without interruption by other threads.
# Ruby does not have built-in atomic types for arbitrary data, # but some operations (like assignment) can be atomic # for simple types. More complex scenarios require locks. -
Revision/versioning of data (Optimistic Concurrency Control): Instead of locking data, each record has a revision number or timestamp. When updating, check if the record has changed since it was read. If it has, restart the operation.
# Example with ActiveRecord::Locking::Optimistic # Add `lock_version` (integer) to your table class Product < ApplicationRecord # ... end product = Product.find(id) # Another thread updates the same product... begin product.price += 10 product.save! # Will generate ActiveRecord::StaleObjectError if lock_version changed rescue ActiveRecord::StaleObjectError # Handle conflict, e.g., reload and retry retry end -
Message brokers / Queues: Instead of direct data access, workers send messages to a broker (e.g., RabbitMQ, Sidekiq with Redis). Message processing occurs sequentially or in a controlled order, reducing conflict likelihood.
-
Use of immutable objects: If data is immutable, concurrent access is safe because no worker can modify it.
The choice of approach or combination depends on data type, performance requirements, and application architecture. In Ruby on Rails web applications, ActiveRecord transactions and optimistic locking are often used for database data, while Mutex is used for in-memory resources. Background tasks (like Sidekiq) typically process messages from queues, which inherently provide some level of ordering.