require_relative '../lib/familia'
require_relative '../lib/familia/validation'
Familia.enable_database_logging = true
Familia.enable_database_counter = true
class Account < Familia::Horreum
identifier_field :account_id
field :account_id
field :balance
field :status
field :last_updated
end
class TransferService
def self.atomic_transfer(from_account, to_account, amount)
from_balance = from_account.balance.to_i - amount
to_balance = to_account.balance.to_i + amount
Familia.transaction do |conn|
conn.hset(from_account.dbkey, 'balance', from_balance.to_s)
conn.hset(to_account.dbkey, 'balance', to_balance.to_s)
conn.hset(from_account.dbkey, 'last_updated', Time.now.to_i.to_s)
conn.hset(to_account.dbkey, 'last_updated', Time.now.to_i.to_s)
end
from_account.balance = from_balance.to_s
to_account.balance = to_balance.to_s
end
def self.non_atomic_transfer(from_account, to_account, amount)
from_account.balance = (from_account.balance.to_i - amount).to_s
to_account.balance = (to_account.balance.to_i + amount).to_s
from_account.save
to_account.save
end
end
puts "๐งช Redis Command Validation Framework Demo"
puts "=" * 50
cleanup_keys = Familia.dbclient.keys("account:*")
Familia.dbclient.del(*cleanup_keys) if cleanup_keys.any?
puts "\n1. Basic Command Recording"
puts "-" * 30
CommandRecorder = Familia::Validation::CommandRecorder
CommandRecorder.start_recording
account = Account.new(account_id: "acc001", balance: "1000", status: "active")
account.save
commands = CommandRecorder.stop_recording
puts "Recorded #{commands.command_count} commands:"
commands.commands.each { |cmd| puts " #{cmd}" }
puts "\n2. Transaction Detection"
puts "-" * 30
CommandRecorder.start_recording
acc1 = Account.new(account_id: "acc002", balance: "2000")
acc2 = Account.new(account_id: "acc003", balance: "500")
acc1.save
acc2.save
TransferService.atomic_transfer(acc1, acc2, 500)
commands = CommandRecorder.stop_recording
puts "Commands executed: #{commands.command_count}"
puts "Transactions detected: #{commands.transaction_count}"
if commands.transaction_blocks.any?
tx = commands.transaction_blocks.first
puts "Transaction commands: #{tx.command_count}"
tx.commands.each { |cmd| puts " [TX] #{cmd}" }
end
puts "\n3. Command Validation with Expectations"
puts "-" * 30
begin
validator = Familia::Validation::Validator.new
result = validator.validate do |expect|
expect.transaction do |tx|
tx.hset("account:acc004:object", "balance", "1500")
.hset("account:acc005:object", "balance", "1000")
.hset("account:acc004:object", "last_updated", Familia::Validation::ArgumentMatcher.new(:any_string))
.hset("account:acc005:object", "last_updated", Familia::Validation::ArgumentMatcher.new(:any_string))
end
acc4 = Account.new(account_id: "acc004", balance: "2000")
acc5 = Account.new(account_id: "acc005", balance: "500")
acc4.save
acc5.save
TransferService.atomic_transfer(acc4, acc5, 500)
end
puts "Validation result: #{result.valid? ? 'PASS โ
' : 'FAIL โ'}"
puts "Summary: #{result.summary}"
rescue => e
puts "Validation demo encountered error: #{e.message}"
puts "This is expected as the framework needs Redis middleware integration"
end
puts "\n4. Performance Analysis"
puts "-" * 30
begin
commands = Familia::Validation.capture_commands do
accounts = []
(1..5).each do |i|
account = Account.new(account_id: "perf#{i}", balance: "1000")
account.save
accounts << account
end
accounts[0].balance = "1100"
accounts[0].save
end
analyzer = Familia::Validation::PerformanceAnalyzer.new(commands)
analysis = analyzer.analyze
puts "Performance Analysis:"
puts " Total Commands: #{analysis[:total_commands]}"
puts " Command Types: #{analysis[:command_type_breakdown].keys.join(', ')}"
puts " Efficiency Score: #{analysis[:efficiency_score]}/100"
rescue => e
puts "Performance analysis encountered error: #{e.message}"
end
puts "\n5. Atomicity Validation"
puts "-" * 30
begin
acc6 = Account.new(account_id: "acc006", balance: "3000")
acc7 = Account.new(account_id: "acc007", balance: "1000")
acc6.save
acc7.save
validator = Familia::Validation::Validator.new(strict_atomicity: true)
commands = validator.capture_redis_commands do
TransferService.atomic_transfer(acc6, acc7, 1000)
end
atomicity_validator = Familia::Validation::AtomicityValidator.new(commands)
result = atomicity_validator.validate
puts "Atomicity validation: #{result.valid? ? 'PASS โ
' : 'FAIL โ'}"
rescue => e
puts "Atomicity validation encountered error: #{e.message}"
end
puts "\n6. Framework Architecture Overview"
puts "-" * 30
puts "
The Redis Command Validation Framework provides:
๐ Command Recording
- Captures all Redis commands with full context
- Tracks transaction boundaries (MULTI/EXEC)
- Records timing and performance metrics
๐ Expectations DSL
- Fluent API for defining expected command sequences
- Support for pattern matching and flexible ordering
- Transaction and pipeline validation
โ
Validation Engine
- Compares actual vs expected commands
- Validates atomicity of operations
- Provides detailed mismatch reports
๐งช Test Helpers
- Integration with tryouts framework
- Methods like assert_redis_commands, assert_atomic_operation
- Automatic setup and cleanup
โก Performance Analysis
- Command efficiency scoring
- N+1 pattern detection
- Transaction overhead analysis
Key Benefits:
โข Brass-tacks Redis command validation
โข Atomic operation verification
โข Performance optimization insights
โข Clear diagnostic messages
โข Thread-safe operation
"
cleanup_keys = Familia.dbclient.keys("account:*")
Familia.dbclient.del(*cleanup_keys) if cleanup_keys.any?
puts "\n๐ Demo complete! The validation framework is ready for use."
puts " See try/validation/ for comprehensive test examples."