Slot Matching
Slot matching is the process by which Prism automatically binds pattern requirements to available backends. This is a key part of Layer 3 (Pattern Selection) and Layer 4 (Backend Binding) in the configuration model.
Slot matching is fully automatic. Users declare requirements, and the platform selects the best backend based on capabilities, performance, cost, and availability.
Overview
What is a Slot?
A slot is a capability requirement that a pattern has. Think of it as a socket that needs to be filled by a compatible backend.
Example: The Multicast Registry pattern has two slots:
registryslot - Needs key-value storage with watch capabilitymessagingslot - Needs pub/sub messaging
Matching Process
1. Pattern declares slot requirements
↓
2. Platform queries backend registry
↓
3. Filter backends by interface compatibility
↓
4. Filter backends by capability support
↓
5. Filter backends by performance requirements
↓
6. Score remaining candidates
↓
7. Select highest-scoring backend
↓
8. Create slot binding
References:
- RFC-056: Unified Configuration Model - Slot binding algorithm
- RFC-039: Backend Configuration Registry - Backend slots section
Slot Requirements
Structure
slot_requirement:
# Slot identity
slot_name: storage
# High-level capabilities needed
capabilities:
- get
- set
- delete
- transaction
# Specific API contracts required
interfaces:
- KeyValueBasicInterface
- TransactionalInterface
# Whether slot is optional
optional: false
# Performance requirements
performance:
min_write_rps: 10000
min_read_rps: 50000
max_latency_p99: 25ms
requires_transactions: true
requires_atomic_writes: true
Common Slots
| Slot Name | Common In Patterns | Typical Requirements |
|---|---|---|
storage | Most patterns | Get, Set, Delete operations |
registry | Multicast Registry | KeyValue + Watch capability |
messaging | PubSub, Queue | Publish, Subscribe |
cache | High-performance patterns | Get, Set with TTL |
wal | Durable patterns | Append-only log |
object_store | Claim-check | Large object storage |
query_store | Mailbox, Search | Complex query support |
graph_store | Graph patterns | Graph traversal |
Matching Algorithm
Phase 1: Interface Compatibility
Required: Backends MUST implement all required interfaces.
def check_interface_compatibility(slot, backend):
"""Check if backend implements required interfaces."""
for required_interface in slot.interfaces:
if required_interface not in backend.interfaces:
return False, f"Missing interface: {required_interface}"
return True, "All interfaces supported"
Example:
# Slot requires
interfaces:
- KeyValueBasicInterface
- TransactionalInterface
# Backend A implements
interfaces:
- KeyValueBasicInterface
- TransactionalInterface
- TTLInterface
# ✅ MATCH (has all required + extra)
# Backend B implements
interfaces:
- KeyValueBasicInterface
# ❌ NO MATCH (missing TransactionalInterface)
Phase 2: Capability Compatibility
Required: Backends MUST support all required capabilities.
def check_capability_compatibility(slot, backend):
"""Check if backend supports required capabilities."""
for required_capability in slot.capabilities:
if required_capability not in backend.capabilities:
return False, f"Missing capability: {required_capability}"
return True, "All capabilities supported"
Example:
# Slot requires
capabilities:
- get
- set
- transaction
# Backend A supports
capabilities:
- get
- set
- delete
- transaction
# ✅ MATCH (has all required)
# Backend B supports
capabilities:
- get
- set
- delete
# ❌ NO MATCH (missing transaction)
Phase 3: Performance Requirements
Required: Backends MUST meet minimum performance thresholds.
def check_performance_requirements(slot, backend):
"""Check if backend meets performance requirements."""
checks = []
# Write throughput
if backend.capacity.available_write_rps < slot.performance.min_write_rps:
return False, f"Insufficient write capacity: {backend.capacity.available_write_rps} < {slot.performance.min_write_rps}"
# Read throughput
if backend.capacity.available_read_rps < slot.performance.min_read_rps:
return False, f"Insufficient read capacity: {backend.capacity.available_read_rps} < {slot.performance.min_read_rps}"
# Latency
if slot.performance.max_latency_p99:
if backend.performance.latency.p99_ms > parse_latency(slot.performance.max_latency_p99):
return False, f"Latency too high: {backend.performance.latency.p99_ms}ms"
# Transactions
if slot.performance.requires_transactions:
if 'transaction' not in backend.capabilities:
return False, "Transactions required but not supported"
return True, "Performance requirements met"
Example:
# Slot requires
performance:
min_write_rps: 10000
min_read_rps: 50000
max_latency_p99: 25ms
# Backend A capacity
capacity:
available_write_rps: 100000
available_read_rps: 500000
performance:
latency:
p99_ms: 20
# ✅ MATCH (exceeds all requirements)
# Backend B capacity
capacity:
available_write_rps: 5000 # Too low!
available_read_rps: 500000
# ❌ NO MATCH (insufficient write capacity)
Phase 4: Scoring
Objective: Among compatible backends, select the best fit based on multiple factors.
def calculate_match_score(slot, backend):
"""
Calculate match score (0.0 to 1.0).
Weights:
- Performance: 40%
- Cost: 30%
- Availability: 20%
- Region affinity: 10%
"""
score = 0.0
# 1. Performance match (40% weight)
write_ratio = min(backend.capacity.available_write_rps / slot.performance.min_write_rps, 2.0)
read_ratio = min(backend.capacity.available_read_rps / slot.performance.min_read_rps, 2.0)
performance_score = min(write_ratio, read_ratio) / 2.0 # 0.5 to 1.0
score += 0.4 * performance_score
# 2. Cost efficiency (30% weight)
# Lower cost per operation = higher score
avg_cost = (backend.cost.cost_per_1k_writes + backend.cost.cost_per_1k_reads) / 2.0
cost_score = 1.0 / (avg_cost * 1000 + 1.0) # Normalize
score += 0.3 * cost_score
# 3. Availability (20% weight)
availability_score = backend.health.uptime_percent_24h / 100.0
score += 0.2 * availability_score
# 4. Region affinity (10% weight)
if backend.region == slot.preferred_region:
score += 0.1
elif backend.region in slot.acceptable_regions:
score += 0.05
return score
Scoring Example:
# Slot requirement
performance:
min_write_rps: 10000
min_read_rps: 50000
preferred_region: us-east-1
# Backend A
capacity:
available_write_rps: 100000 # 10x required
available_read_rps: 500000 # 10x required
cost:
cost_per_1k_writes: 0.001
cost_per_1k_reads: 0.0005
health:
uptime_percent_24h: 99.95
region: us-east-1
# Score calculation:
# Performance: (10x / 2) / 2 = 1.0 → 0.4 * 1.0 = 0.40
# Cost: 1.0 / (0.00075 * 1000 + 1) = 0.57 → 0.3 * 0.57 = 0.17
# Availability: 99.95 / 100 = 0.9995 → 0.2 * 0.9995 = 0.20
# Region: Match → 0.1
# TOTAL: 0.87
# Backend B
capacity:
available_write_rps: 15000 # 1.5x required
available_read_rps: 75000 # 1.5x required
cost:
cost_per_1k_writes: 0.0001 # 10x cheaper!
cost_per_1k_reads: 0.00005
health:
uptime_percent_24h: 99.50
region: us-west-2
# Score calculation:
# Performance: (1.5x / 2) / 2 = 0.375 → 0.4 * 0.375 = 0.15
# Cost: 1.0 / (0.000075 * 1000 + 1) = 0.93 → 0.3 * 0.93 = 0.28
# Availability: 99.50 / 100 = 0.995 → 0.2 * 0.995 = 0.20
# Region: Different → 0.0
# TOTAL: 0.63
# Backend A wins (0.87 > 0.63)
Match Reasons
When a backend is selected, the platform generates reasons explaining why:
Reason Types
slot_binding:
pattern_id: wal-001
slot_name: storage
backend_id: postgres-primary
reasons:
- type: capability_match
explanation: "Backend implements TransactionalInterface required for WAL"
- type: performance
explanation: "Backend provides 100k write RPS (required: 10k, 10x headroom)"
- type: consistency
explanation: "Backend offers strong consistency (matches durability: strong)"
- type: region_affinity
explanation: "Backend in same region (us-east-1) as requester"
- type: cost
explanation: "Backend has competitive cost ($0.001 per 1k writes)"
Rejection Reasons
When a backend is NOT selected:
alternative:
backend_id: redis-cache
score: 0.45
rejection_reason: "Lower score (0.45) than selected backend (0.87)"
issues:
- "Missing TransactionalInterface"
- "No ACID transaction support"
Advanced Matching
Multi-Slot Patterns
Patterns with multiple slots require coordinated matching:
pattern: multicast-registry
slots:
registry:
capabilities: [get, set, delete, watch]
interfaces: [KeyValueBasicInterface, WatchInterface]
performance:
min_write_rps: 1000
min_read_rps: 10000
messaging:
capabilities: [publish, subscribe]
interfaces: [PubSubInterface]
performance:
min_write_rps: 5000
min_read_rps: 50000
# Matched bindings
slot_bindings:
- slot_name: registry
backend_id: postgres-primary
reasons: [capability_match, watch_support]
- slot_name: messaging
backend_id: kafka-prod
reasons: [capability_match, high_throughput]
Co-Location Optimization
For performance, slots may prefer backends in the same region:
optimization:
prefer_colocation: true
max_latency_between_slots: 5ms
# This may lower individual slot scores but improve overall performance
slot_bindings:
- slot_name: registry
backend_id: postgres-us-east-1 # Score: 0.82
reason: "Co-located with messaging backend"
- slot_name: messaging
backend_id: kafka-us-east-1 # Score: 0.85
reason: "Co-located with registry backend"
# Better than:
# registry: postgres-eu-west-1 (score: 0.90)
# messaging: kafka-us-east-1 (score: 0.85)
# Cross-region latency: 80ms
Fallback Strategies
Capacity Exhaustion
When primary backends are at capacity:
slot: storage
primary_match:
backend_id: postgres-primary
rejection_reason: "Capacity exhausted (100k/100k write RPS used)"
fallback_match:
backend_id: postgres-secondary
score: 0.72
reason: "Fallback to secondary (lower score but available capacity)"
Regional Fallback
When preferred region unavailable:
slot: storage
preferred_region: us-east-1
primary_match:
backend_id: postgres-us-east-1
rejection_reason: "Unhealthy status"
fallback_match:
backend_id: postgres-us-west-2
score: 0.75
reason: "Fallback to different region (same compliance: PCI, SOC2)"
Degraded Mode
When no exact match exists:
slot: storage
required_interfaces:
- KeyValueBasicInterface
- TransactionalInterface
degraded_match:
backend_id: redis-cache
score: 0.60
warnings:
- "Missing TransactionalInterface"
- "No ACID transaction support"
reason: "Degraded mode: Some features unavailable"
Match Validation
Before finalizing a binding, the platform validates:
1. Interface Contract Validation
validation:
slot: storage
backend: postgres-primary
checks:
- interface: KeyValueBasicInterface
operations:
- Get: ✅ Implemented
- Set: ✅ Implemented
- Delete: ✅ Implemented
- Exists: ✅ Implemented
- interface: TransactionalInterface
operations:
- BeginTransaction: ✅ Implemented
- CommitTransaction: ✅ Implemented
- RollbackTransaction: ✅ Implemented
2. Quota Validation
validation:
team: payments-team
current_usage:
total_write_rps: 40000
new_request:
write_rps: 15000
team_quota:
max_total_write_rps: 50000
result: ❌ DENIED
reason: "Would exceed team quota (40k + 15k > 50k)"
3. Compliance Validation
validation:
namespace_requirements:
compliance: [pci, hipaa]
backend_compliance:
backend: postgres-primary
certifications: [pci, soc2, hipaa]
result: ✅ APPROVED
reason: "Backend has all required compliance certifications"
Performance Considerations
Matching Speed
Slot matching must be fast (< 100ms) to avoid blocking namespace creation:
# Optimization strategies:
1. Index backends by interfaces (hash map lookup)
2. Index backends by capabilities (hash map lookup)
3. Pre-calculate backend scores (cache for 5 minutes)
4. Parallel evaluation of candidates
5. Early termination when perfect match found
Caching
match_cache:
key: "{slot_requirements_hash}:{available_backends_hash}"
ttl: 300s # 5 minutes
invalidate_on:
- backend_capacity_change
- backend_health_change
- new_backend_registered
Monitoring
metrics:
- match_duration_ms
- candidates_evaluated
- cache_hit_rate
- match_score_distribution
- rejections_by_reason
Example: Complete Matching Flow
Input
namespace_request:
namespace: order-processing
team: payments-team
pattern: queue
needs:
durability: strong
write_rps: 5000
read_rps: 10000
consistency: strong
Pattern Selection Output
selected_patterns:
- pattern_id: wal-001
pattern_type: wal
slot_requirements:
- slot_name: storage
capabilities: [get, set, delete, transaction]
interfaces: [KeyValueBasicInterface, TransactionalInterface]
performance:
min_write_rps: 5000
min_read_rps: 10000
requires_transactions: true
Backend Query
query:
required_interfaces:
- KeyValueBasicInterface
- TransactionalInterface
required_capabilities:
- transaction
min_capacity:
min_write_rps: 5000
min_read_rps: 10000
Candidates
candidates:
- backend_id: postgres-primary
score: 0.87
interfaces: ✅ All match
capabilities: ✅ All match
performance: ✅ 100k/500k RPS
cost: $0.001 per 1k writes
health: 99.95% uptime
region: us-east-1
- backend_id: postgres-secondary
score: 0.72
interfaces: ✅ All match
capabilities: ✅ All match
performance: ✅ 50k/200k RPS
cost: $0.001 per 1k writes
health: 99.90% uptime
region: us-west-2
- backend_id: mysql-prod
score: 0.68
interfaces: ✅ All match
capabilities: ✅ All match
performance: ⚠️ 20k/100k RPS (lower)
cost: $0.002 per 1k writes (higher)
health: 99.80% uptime
region: us-east-1
Selected Binding
slot_binding:
pattern_id: wal-001
slot_name: storage
backend_id: postgres-primary
reasons:
- type: capability_match
explanation: "Implements TransactionalInterface required for WAL"
- type: performance
explanation: "Provides 100k write RPS (required: 5k, 20x headroom)"
- type: consistency
explanation: "Strong consistency matches durability requirement"
- type: availability
explanation: "99.95% uptime in last 24h"
- type: region_affinity
explanation: "Located in us-east-1 (same as requester)"
slot_config:
connection_pool_size: 20
query_timeout: 5s
retry_policy:
max_attempts: 3
Alternatives Considered
alternatives:
- backend_id: postgres-secondary
score: 0.72
rejection_reason: "Lower score (0.72 vs 0.87)"
notes: "Would be selected if primary unavailable"
- backend_id: mysql-prod
score: 0.68
rejection_reason: "Lower score (0.68 vs 0.87)"
notes: "Lower performance, higher cost"
See Also
- Backend Registry - Backend configuration
- Configuration Overview - 6-layer model
- RFC-056: Unified Configuration Model
- RFC-039: Backend Configuration Registry
- MEMO-006: Backend Interface Decomposition