-
Notifications
You must be signed in to change notification settings - Fork 0
Storage Thread Safety Guide
The storage system is designed to be thread-safe and to prevent deadlocks in concurrent environments. All operations are protected by mutex locks to ensure thread safety, and special attention has been paid to preventing deadlocks.
All storage implementations use mutex locks to protect access to shared resources. This ensures that operations are thread-safe and that data is not corrupted by concurrent access.
Some synchronous methods in the storage managers (InMemoryStorageManager and FileStorageManager) use kotlinx.coroutines.runBlocking to make these methods thread-safe by acquiring a mutex lock. This is necessary because these methods are part of interfaces that cannot be changed to use suspend functions without breaking backward compatibility.
The following methods use runBlocking for thread safety:
getStorage()registerStorage()unregisterStorage()isActive()
Justification: While the project generally aims to avoid blocking calls in favor of coroutines, these specific uses of runBlocking are justified because:
- They are used in synchronous interface methods that cannot be changed to suspend functions
- They only block for a very short time to acquire a mutex lock and perform a simple operation
- They are well-documented in the code with comments explaining their purpose
- They follow best practices for deadlock prevention by minimizing the scope of the lock
Note: For new code, it's recommended to use suspend functions and coroutines instead of blocking calls whenever possible.
protected val mutex = Mutex()
suspend fun store(key: K, value: V, metadata: Map<String, Any>): Boolean {
return mutex.withLock {
storage[key] = Pair(value, metadata.toMutableMap())
true
}
}To prevent deadlocks, the storage system follows these best practices:
Only lock the mutex for the minimum time necessary to ensure thread safety. Perform as much work as possible outside the lock.
// Bad example (lock held for too long)
suspend fun setActorState(actorId: String, state: ActorState): Boolean {
return mutex.withLock {
val actorData = retrieve(actorId)?.first?.toMutableMap() ?: mutableMapOf()
val metadata = retrieve(actorId)?.second?.toMutableMap() ?: mutableMapOf()
// Make changes to actorData
store(actorId, actorData, metadata)
}
}
// Good example (minimize lock scope)
suspend fun setActorState(actorId: String, state: ActorState): Boolean {
// Retrieve data outside the mutex lock
val retrievedData = retrieve(actorId)
val actorData = retrievedData?.first?.toMutableMap() ?: mutableMapOf()
val metadata = retrievedData?.second?.toMutableMap() ?: mutableMapOf()
// Create state data outside the lock
val stateData = when (state) {
is ActorState.Initialized -> mapOf("type" to "Initialized")
is ActorState.Running -> mapOf("type" to "Running")
is ActorState.Stopped -> mapOf("type" to "Stopped")
is ActorState.Error -> mapOf("type" to "Error", "exception" to state.exception)
is ActorState.Paused -> mapOf("type" to "Paused", "reason" to state.reason)
}
// Set state in actor data outside the lock
actorData["state"] = stateData
// Update the storage with mutex lock
return mutex.withLock {
storage[actorId] = Pair(actorData, metadata)
true
}
}Never call a method that acquires the same lock from within a locked block. If you need to call such methods, do so outside the lock.
// Bad example (nested locks)
mutex.withLock {
// This will cause a deadlock if retrieve() also acquires the same mutex
val data = retrieve(key)
// ...
}
// Good example (avoid nested locks)
// Call retrieve() outside the lock
val data = retrieve(key)
mutex.withLock {
// Use the retrieved data inside the lock
// ...
}In some cases, it may be appropriate to directly access the protected resource instead of calling methods that acquire locks. This should be done carefully and only when necessary.
// Instead of this (which may cause deadlocks):
mutex.withLock {
val data = retrieve(key)
// ...
store(key, updatedData)
}
// Do this:
val data = retrieve(key)
// ...
mutex.withLock {
storage[key] = updatedData
}Add error handling to prevent hanging if something goes wrong. Use try-catch blocks to catch exceptions and release locks properly.
return try {
// Retrieve data outside the mutex lock
val retrievedData = retrieve(key)
// ...
mutex.withLock {
// Update storage
true
}
} catch (e: Exception) {
// Log the error
false
}SolaceCore SSOT wiki · published from wiki/ by .github/workflows/publish-wiki.yml · edit the source in the repo, not the wiki.
Orientation
- Architectural Deep Dive
- Architecture Overview
- Design vs Implementation
- Framework Actor System
- Framework Architectural Vision
- Framework Concurrency and Communication
- Framework Data Storage and Management
- Framework Deployment and Containerization
- Framework Development Roadmap
- Framework Hot-Pluggable System
- Framework Implementation Status
- Framework Observability and Monitoring
- Framework Port System
- Framework System Architecture
- Framework Workflow Management
- Project Status
- Project Status Report
- Quick Status
- Solace Core Framework Architecture
- SolaceCore Architecture Overview
- Vision & Solace AI
Runtime
- Actor Builder
- Actor Communication Sequence Diagram
- Actor Core Definitions
- Actor Graph View
- Actor Metrics
- Actor Module Architecture
- Actor Queue Hibernation and Correlation
- Actor Roadmap
- Actor State Recovery Subsystem
- Actor State Serialization Subsystem
- Actor Supervision Module
- Actor System Architecture
- Actor System Class Diagram
- Actor Usage Examples
- Compose App Features
- JVM Scripting Implementations
- Kernel & Ports
- Kernel Channel System
- Kernel Future Enhancements
- Kernel Module Architecture
- Kernel Port Implementations and Exceptions
- Kernel Port Usage Example
- Kernel Testing Strategy
- Lifecycle Class Diagram
- Lifecycle Management Architecture
- Pipeline DSL
- Real-Time UI Implementation
- Scripting Module Architecture
- Scripting Module Design
- Scripting Supporting Components
- Shared Memory
- Storage & Persistence
- Storage Abstractions Architecture
- Storage Caching Subsystem
- Storage Checklist
- Storage Compression Subsystem
- Storage Core Interfaces
- Storage Encryption Subsystem
- Storage File-Based Architecture
- Storage File-Based Implementations
- Storage In-Memory Architecture
- Storage In-Memory Implementations
- Storage JVM Serialization Utilities
- Storage Module Architecture
- Storage Serialization Compression Encryption
- Storage Specialized Interfaces Architecture
- Storage Status and Future Plans
- Storage Testing
- Storage Thread Safety Guide
- Storage Thread Safety and Deadlock Prevention
- Storage Transactions
- Storage Usage Examples
- Supervisor and Hot Swap
- SupervisorActor
- System Architecture Diagram
- Workflow Management Architecture
- Workflow Management Design Concept
- Workflow Orchestration
Solace AI
- Confusion Corrector
- Inference Cube
- Inference Cube Technical Architecture
- Long-Term Memory
- MCP and Tool Format
- Memory & Reflection
- Memory Compression
- Memory Feature Overview
- Memory Retrieval
- Mood & Emotional Model
- Mood Module Implementation
- Mouth Tool Technical Spec
- Multimodal Nudging
- Perception Actors
- Provider Specs
- Reflection Memory
- Solace AI Overview
- Supervisor AI
- Supervisor Emotional Model Integration
- Time Actor
- Voice & Mouth Tool
- Working Memory
- Zoom Level Technical Spec
- Zoom Levels
Reference
- Advanced Workflow Example
- Basic Actor Usage
- Build System and Dependencies
- Development Tooling and Practices
- Documentation Catalog
- Documentation Index
- Feature Index
- Glossary
- How the Wiki Publishes
- JVM Utilities
- Kotlin Implementation Details
- Kotlin-Aligned Architecture Overview
- Kotlin-Aligned Contributing
- Kotlin-Aligned Core Architectural Principles
- Kotlin-Aligned Daily Development Workflow
- Kotlin-Aligned Development Examples
- Kotlin-Aligned Development Workflow
- Kotlin-Aligned Documentation
- Kotlin-Aligned Implementation Status
- Kotlin-Aligned Key Concepts
- Kotlin-Aligned Known Issues
- Kotlin-Aligned Quick Start
- Kotlin-Aligned Running the System
- Kotlin-Aligned System Architecture
- LangChain Actor Code Changes
- LangChain Actor Usage Improvements
- LangChain ActorInterface Code Changes
- LangChain Best Practices
- LangChain Bugs
- LangChain Chain Implementation
- LangChain Code Changes
- LangChain Code Changes Rollout and Impact
- LangChain Configuration Management Improvements
- LangChain Configuration Recommendations
- LangChain Core Architecture Recommendations
- LangChain Directory Structure Changes
- LangChain Documentation Improvements
- LangChain Dynamic Wiring Rollout Notes
- LangChain Fix Proposal
- LangChain Implementation Priorities
- LangChain Lifecycle Management Improvements
- LangChain Memory Integration Recommendations
- LangChain Metrics and Observability Recommendations
- LangChain Migration Strategy
- LangChain New Files Needed
- LangChain New Packages to Add
- LangChain Package-by-Package Improvements
- LangChain Patterns
- LangChain Port Code Changes
- LangChain Port System Recommendations
- LangChain Port Usability Improvements
- LangChain Prompt Management Recommendations
- LangChain Recommendations
- LangChain Recommendations Rollout Plan
- LangChain Required Interface Changes
- LangChain Testing Changes
- LangChain Testing Improvements
- LangChain Testing Recommendations
- LangChain Tool Integration Recommendations
- LangChain Type-Safe Dynamic Wiring
- LangChain Type-Safe Dynamic Wiring System
- LangChain Usage Design Improvements
- Master Checklist
- Roadmap
- Roadmap Issues
- Roadmap Phase 1 Stability and Testing
- Roadmap Phase 2 Production Infrastructure
- Roadmap Phase 3 Documentation and Developer Experience
- Roadmap Phase 4 Graph Database Integration
- Roadmap Phase 5 Security Framework
- Roadmap Phase 6 Distributed System
- Roadmap Phase 7 Advanced Features
- Roadmap Phase 8 Ecosystem Development
- Roadmap Timeline and Success Metrics
- Setup Instructions
- Sketch Architecture
- Status Documentation
- Task 1 Core Tests
- Task 2 Connection Wiring
- Task 3 Concurrency Issues
- Task 4 Dynamic Registration
- Task 5 Integration Tests
- Task 6 Deadlock Detection
- Task Documentation
- Test Coverage Checklist
- Testing Strategy