Software Transactional Memory #307
Unanswered
felsweg-iota
asked this question in
RFC
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
software transactional memory
)Summary
Replace
actix
with a software transactional memory (STM) based system. Keeping concurrent calls and atomic transactions transparent to the user.This RFC shall give insight on
Motivation
Removal of Actix as actor system
Stronghold employs
actix
as actor framework to manage concurrent operations inside the underlying system. While an actor system is not a bad choice, as it abstracts away difficult synchronization mechanisms, actix explicitly takes ownership of the underlying executor framework, which in turn makes it hard to integrate Stronghold in a shared context. Furthermoreactix
runs on a single threaded event loop, that renders actor isolation per thread obsolete.Advantages over actor systems
In an STM based system all objects having mutable state are transactional, the behavior on the objects are transparently using the underlying system. This allows to isolate guarded memory from being exposed at runtime. Transactions are always safe; internal conflicts are rolled back automatically and retried until the transaction succeeds. Operations on mutable memory are composable.
Since only write operations actually change the object, this operation must be communicated to the other threads. Recent work describes multiple approaches, where we consider blocking / retrying other transaction the most viable approach to ensure data consistency. if the transaction has been finished, while having all read operations validated, the resulting work is committed to the actual object. Other threads operating on the same object, never see the changes done to this object. The STM described here, uses a lazy approach in rolling back a transaction An STM can also be described as an optimistic locking approach: Work on memory is considered safe until a conflict occurs, the transaction can be safely rolled back and retried.
Apriori Problems with Composable Transaction
Every transaction are only allowed to non-irrevocably operations
The example above shows an io operation, that is irrevocably for any transaction log. Thus each transaction log should only make benign memory modifications.
Blocking does not compose
Guide-level explanation
Overview
A Software transactional memory (STM) offers composable concurrency constructs. Bundling transactions to memory in an atomic way. A target object, that can be potentially be changed by concurrent processes is being cloned. Each read or write operation on this object is being recorded in a thread local copy of this object.
System Description
The following paragraph shortly describes the system, that an STM tries to support.
Use Cases
Use cases shown below should illustrate, that writes, reads and execution of procedures are done atomically within the runtime. Minimal API changes have been introduced to showcase the integration of an STM.
Use cases with explicit atomic transactions
The use cases shown here use either explicit calls to an
atomic
function, that wraps a transaction to all referenced variables, or use an attribute macro to mark a function explicitly astransactional
.Ephemeral key derivation and encryption
Use case(s) with implicit atomic transactions
The use cases shown below assume some implicit / blanket implementations of transactional traits. Please note, that the traits mentioned here are for demonstration purposes only, and shall give an idea how an transactional memory system could be implemented. A more formal description of proper types and traits is out of scope for this RFC.
Writing multiple records into the vault and write a snapshot
This code example loads a client from a path, a new task is being spawned, that references
the client to write some data into the vault, at the end the current state will be persisted
inside a snapshot. Even though writing to the vault seemingly is being executed concurrently,
the execution itself is atomic: the state is written BEFORE the state will be written into the
snapshot. No data races occur, clients can be modified concurrently in a safe way.
Reference-level explanation
In order to make transactions atomic, we need to ensure that each transaction has a thread local copy of the target variable. One way to do that is to keep a log with a value. The object itself is referenced by a
TVar
or transactional variable, that can be shared across threads (or in case of asynchronous runtime: tasks). EachTVar
keeps track of it's own thread and all other threads, that potentially want to update the variable viaVarAccessControl
. Reading from a variable might involve some validation, if not other thread has been written to the same variable. In case it had, the transaction will be retried ( standard case,one can incorporate some counter here to mitigate possible infinite retries). The whole transaction is being reflected by aTransaction
object, that manages the execution and finally commits the calculated value to memory.Another approach would be a blanket implementation for all types, that satisfy a
Transactional
trait. Using this approach, sensible defaults can be inferred from the types used to ensure transactional memory.Integration Into Existing Stronghold Types
The following subsections describe possible integration of transactional memory on existing types and components. Each subsection discusses possible integrations and their issues, and ideally how to overcome them.
Transactional Clients
Client
s should be able to work in concurrent contexts, this their inner state should remain consistent, providing an atomic reference to their objects, so the inner state will not be copied.Transactional Vaults
Vault
s can be accessed viaDbView
. EachVault
can be locked in aTVar
, thus isolating access to the state itself, exposing it to modifications viaTLog
. SinceClient
s themselves should not directly be transactional, each atomic modification of theVault
s state should be done here.TVar
differs here fromTLog
, since the vault entries themselves are protected.Transactional Snapshots
Regarding
Snapshots
we need to distinguish betweenSnapshots
retained encrypted in memory, andSnapshot
-files. Since the former can be directly put into a transaction, the latter could write non-revertible state into the file, possibly inducing to keep a log of operations itself, which is not desirable. Instead of putting the write ( read "io" here ) operations into a transaction, a successful commit to theSnapshot
state could trigger an atomic write into theSnapshot
-file itself to always have a consistent state of the snapshot file.Transactional Procedures
Procedures mainly rely on secrets inside the
Vault
, each operation shall make use of the transactions.Security
Since working with transactional memory involves writing
designated
state into atarget
state, thedesignated
state written into and stored by aTLog
must be memory secure. The STM makes use of the underlying protection mechanism, provided by the new runtime RFC. The interface to obtain secured memory regions (non-contiguous) looks roughly like this:Integration of
SecureMemory
As already outlined above,
TLog
andTVar
are strongly bound toSecureMemory
. Eachread
andwrite
operation on the respective types, make heavy use the secure memory api. This would induce a performance penalty as unlocking and decrypting would take a certain amount of time, but each memory operation would be secure by default.Drawbacks
Why should we not do this?
Since STM use optimistic locking, long transaction could hang / retry for a very long time. This can be mitigated that by using a similar construct to a circuit breaker, the retries can fail after a number of times returning error from the underlying transaction.
Rationale and alternatives
Why is this design the best in the space of possible designs?
STM ease the use of concurrent processes, offer the user a more natural way to call functionality in a safe way. Employing an STM also makes the inner workings of synchronizing transparent.
What other designs have been considered and what is the rationale for not choosing them?
Another approach for handling concurrent processes is an actor system like actix. The prior framework riker has been deprecated, while actix takes over the asynchronous runtime, that makes the integration of Stronghold into existing applications security aware, moves the responsibility of synchronization back to the implementer.
What is the impact of not doing this?
The responsibility to operate Stronghold safely in a concurrent environment will still be shifted to the developer. Even though Stronghold can is being used in concurrent system, it does not offer the benefits of a multi tenant system itself.
Unresolved questions
Future possibilities
none so far.
Resources
Beta Was this translation helpful? Give feedback.
All reactions