Implementing Transactional Memory in JavaScript
Transactional memory allows you to group a series of operations into a single atomic unit. If any operation within the transaction fails, all changes are rolled back, ensuring data consistency. This challenge asks you to implement a simplified transactional memory system in JavaScript, enabling you to perform multiple state updates as if they happened instantaneously.
Problem Description
You are tasked with creating a TransactionalMemory class in JavaScript. This class should provide a mechanism to group state updates within a transaction. The core functionality revolves around the run method, which accepts a function. This function represents the transaction's logic and can modify the internal state of the TransactionalMemory instance. The run method should:
- Begin a Transaction: Before executing the transaction function, save the current state of the object.
- Execute the Transaction: Execute the provided function.
- Commit or Rollback: If the transaction function completes successfully (doesn't throw an error), commit the changes made during the transaction. If the transaction function throws an error, rollback to the state saved before the transaction began.
- Return a Value: The
runmethod should return the value returned by the transaction function.
The internal state of the TransactionalMemory is represented by a simple object. The transaction function can modify this object.
Examples
Example 1:
Input:
const tm = new TransactionalMemory({ a: 1, b: 2 });
tm.run(() => {
tm.state.a = 10;
tm.state.b = 20;
return 42;
});
Output:
{ a: 10, b: 20 }
Explanation:
The transaction successfully modified the state. The `run` method returns 42.
Example 2:
Input:
const tm = new TransactionalMemory({ a: 1, b: 2 });
tm.run(() => {
tm.state.a = 10;
throw new Error("Transaction failed!");
});
Output:
{ a: 1, b: 2 }
Explanation:
The transaction failed due to an error. The state is rolled back to its original value.
Example 3: (Edge Case - Empty Transaction)
Input:
const tm = new TransactionalMemory({ a: 1, b: 2 });
tm.run(() => {
return 123;
});
Output:
{ a: 1, b: 2 }
Explanation:
The transaction didn't modify the state, but it still needs to be wrapped in the transactional logic. The `run` method returns 123.
Constraints
- The
TransactionalMemoryclass should be implemented using JavaScript. - The internal state of the
TransactionalMemoryis a plain JavaScript object. - The transaction function can modify the internal state.
- The
runmethod must handle errors thrown by the transaction function by rolling back the state. - The
runmethod must return the value returned by the transaction function. - The initial state of the
TransactionalMemoryis provided in the constructor.
Notes
- Consider using
try...catchblocks to handle errors within therunmethod. - Deep copying the state before and after the transaction is crucial for proper rollback. Using
JSON.parse(JSON.stringify(obj))is a simple way to achieve a deep copy, but be aware of its limitations (e.g., functions and circular references won't be copied). For more complex scenarios, consider a dedicated deep copy library. - This is a simplified implementation of transactional memory. Real-world transactional memory systems are significantly more complex and often involve concurrency control mechanisms.
- Focus on the core functionality of committing or rolling back changes based on the success or failure of the transaction function.
class TransactionalMemory {
constructor(initialState) {
this.state = initialState;
}
run(transaction) {
const previousState = JSON.parse(JSON.stringify(this.state)); // Deep copy
try {
const result = transaction();
this.state = JSON.parse(JSON.stringify(this.state)); // Deep copy after modification
return result;
} catch (error) {
this.state = previousState; // Rollback
throw error; // Re-throw the error to propagate it
}
}
}