cs-element
Version:
Advanced reactive data management library with state machines, blueprints, persistence, compression, networking, and multithreading support
702 lines (581 loc) • 24.8 kB
Markdown
# 🔒 Транзакции и блокировки
CSElement предоставляет мощную систему транзакций с поддержкой ACID-свойств и управления блокировками для обеспечения целостности данных.
## 📋 Содержание
- [Основы транзакций](#основы-транзакций)
- [Система блокировок](#система-блокировок)
- [Уровни изоляции](#уровни-изоляции)
- [Точки сохранения](#точки-сохранения)
- [Обнаружение взаимоблокировок](#обнаружение-взаимоблокировок)
- [Полный пример](#полный-пример-банковская-система)
## Основы транзакций
### Создание и управление транзакциями
```typescript
import {
TransactionManagerImpl,
LockManagerImpl,
TransactionConfig,
IsolationLevel,
TransactionOperationType
} from 'cs-element';
// Создание менеджеров
const lockManager = new LockManagerImpl();
const transactionManager = new TransactionManagerImpl(lockManager);
// Простая транзакция
const transaction = await transactionManager.begin();
try {
// Выполняем операции
await transaction.addOperation({
id: 'op1',
type: TransactionOperationType.UPDATE,
elementId: 'user-123',
timestamp: Date.now(),
execute: async () => {
// Обновляем данные пользователя
const user = CSElement.getElementById('user-123');
await user.setData('balance', 1000);
return 'success';
},
rollback: async () => {
// Откат изменений
const user = CSElement.getElementById('user-123');
await user.setData('balance', 500);
}
});
// Коммитим транзакцию
await transaction.commit();
console.log('✅ Транзакция успешно завершена');
} catch (error) {
// Откатываем при ошибке
await transaction.rollback();
console.error('❌ Транзакция отменена:', error);
}
```
### Конфигурация транзакций
```typescript
// Расширенная конфигурация
const config: TransactionConfig = {
isolationLevel: IsolationLevel.SERIALIZABLE,
timeout: 30000, // 30 секунд
maxOperations: 100,
enableLogging: true,
enableProfiling: true,
autoCommit: false,
readOnly: false
};
const transaction = await transactionManager.begin(config);
// Получение информации о транзакции
const info = transaction.getInfo();
console.log('Информация о транзакции:', {
id: info.id,
status: info.status,
operationsCount: info.operationsCount,
duration: info.duration,
isolationLevel: info.isolationLevel
});
```
### Функциональный подход
```typescript
// Выполнение операций в контексте транзакции
const result = await transactionManager.withTransaction(async (tx) => {
// Операция 1: Списание с одного счета
await tx.addOperation({
id: 'debit',
type: TransactionOperationType.UPDATE,
elementId: 'account-from',
execute: async () => {
const account = CSElement.getElementById('account-from');
const balance = await account.getData('balance') as number;
if (balance < 100) {
throw new Error('Недостаточно средств');
}
await account.setData('balance', balance - 100);
return balance - 100;
},
rollback: async () => {
const account = CSElement.getElementById('account-from');
const balance = await account.getData('balance') as number;
await account.setData('balance', balance + 100);
}
});
// Операция 2: Зачисление на другой счет
await tx.addOperation({
id: 'credit',
type: TransactionOperationType.UPDATE,
elementId: 'account-to',
execute: async () => {
const account = CSElement.getElementById('account-to');
const balance = await account.getData('balance') as number;
await account.setData('balance', balance + 100);
return balance + 100;
},
rollback: async () => {
const account = CSElement.getElementById('account-to');
const balance = await account.getData('balance') as number;
await account.setData('balance', balance - 100);
}
});
return 'Перевод выполнен успешно';
}, {
isolationLevel: IsolationLevel.SERIALIZABLE,
timeout: 15000
});
console.log('Результат:', result);
```
## Система блокировок
### Типы блокировок
```typescript
// Read-блокировка (разделяемая)
const readLockAcquired = await lockManager.acquireLock(
'element-123',
'transaction-1',
'read'
);
// Write-блокировка (эксклюзивная)
const writeLockAcquired = await lockManager.acquireLock(
'element-123',
'transaction-2',
'write'
);
// Проверка блокировки
const isLocked = lockManager.isLocked('element-123');
console.log('Элемент заблокирован:', isLocked);
// Информация о блокировке
const lockInfo = lockManager.getLockInfo('element-123');
console.log('Информация о блокировке:', {
locked: lockInfo.locked,
lockType: lockInfo.lockType,
transactionId: lockInfo.transactionId,
acquiredAt: lockInfo.acquiredAt,
waitingRequests: lockInfo.waitingRequests
});
```
### Управление блокировками
```typescript
class SmartLockManager {
private lockManager: LockManagerImpl;
private lockTimeouts: Map<string, NodeJS.Timeout> = new Map();
constructor(lockManager: LockManagerImpl) {
this.lockManager = lockManager;
}
async acquireLockWithTimeout(
elementId: string,
transactionId: string,
lockType: 'read' | 'write',
timeoutMs: number = 5000
): Promise<boolean> {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error(`Таймаут получения блокировки для ${elementId}`));
}, timeoutMs);
this.lockManager.acquireLock(elementId, transactionId, lockType)
.then(acquired => {
clearTimeout(timeout);
if (acquired) {
this.lockTimeouts.set(`${elementId}-${transactionId}`, timeout);
}
resolve(acquired);
})
.catch(error => {
clearTimeout(timeout);
reject(error);
});
});
}
async releaseLockSafely(elementId: string, transactionId: string): Promise<boolean> {
const key = `${elementId}-${transactionId}`;
const timeout = this.lockTimeouts.get(key);
if (timeout) {
clearTimeout(timeout);
this.lockTimeouts.delete(key);
}
return await this.lockManager.releaseLock(elementId, transactionId);
}
async releaseAllLocksForTransaction(transactionId: string): Promise<number> {
// Очищаем все таймауты для транзакции
for (const [key, timeout] of this.lockTimeouts) {
if (key.endsWith(`-${transactionId}`)) {
clearTimeout(timeout);
this.lockTimeouts.delete(key);
}
}
return await this.lockManager.releaseAllLocks(transactionId);
}
}
```
## Уровни изоляции
### READ_UNCOMMITTED
```typescript
// Самый низкий уровень изоляции
const transaction = await transactionManager.begin({
isolationLevel: IsolationLevel.READ_UNCOMMITTED
});
// Может читать незафиксированные изменения других транзакций
// Возможны грязные чтения, неповторяемые чтения, фантомные чтения
```
### READ_COMMITTED
```typescript
// Предотвращает грязные чтения
const transaction = await transactionManager.begin({
isolationLevel: IsolationLevel.READ_COMMITTED
});
// Читает только зафиксированные данные
// Возможны неповторяемые чтения и фантомные чтения
```
### REPEATABLE_READ
```typescript
// Предотвращает грязные и неповторяемые чтения
const transaction = await transactionManager.begin({
isolationLevel: IsolationLevel.REPEATABLE_READ
});
// Гарантирует, что повторное чтение вернет те же данные
// Возможны фантомные чтения
```
### SERIALIZABLE
```typescript
// Самый высокий уровень изоляции
const transaction = await transactionManager.begin({
isolationLevel: IsolationLevel.SERIALIZABLE
});
// Полная изоляция, как будто транзакции выполняются последовательно
// Предотвращает все аномалии чтения
```
## Точки сохранения
### Создание и использование
```typescript
const transaction = await transactionManager.begin();
try {
// Выполняем первую группу операций
await transaction.addOperation({
id: 'op1',
type: TransactionOperationType.CREATE,
elementId: 'new-user',
execute: async () => {
const user = new CSElement('new-user');
await user.setData('name', 'John Doe');
return 'user created';
},
rollback: async () => {
// Удаляем созданного пользователя
CSElement.removeElement('new-user');
}
});
// Создаем точку сохранения
const savepoint1 = await transaction.createSavepoint('after-user-creation');
console.log('✅ Точка сохранения создана:', savepoint1.name);
// Выполняем вторую группу операций
await transaction.addOperation({
id: 'op2',
type: TransactionOperationType.UPDATE,
elementId: 'new-user',
execute: async () => {
const user = CSElement.getElementById('new-user');
await user.setData('email', 'invalid-email'); // Некорректный email
return 'email set';
},
rollback: async () => {
const user = CSElement.getElementById('new-user');
await user.deleteData('email');
}
});
// Валидация email
const user = CSElement.getElementById('new-user');
const email = await user.getData('email') as string;
if (!email.includes('@')) {
// Откатываемся к точке сохранения
await transaction.rollbackToSavepoint(savepoint1.id);
console.log('🔄 Откат к точке сохранения после ошибки валидации');
// Исправляем email
await transaction.addOperation({
id: 'op2-fixed',
type: TransactionOperationType.UPDATE,
elementId: 'new-user',
execute: async () => {
const user = CSElement.getElementById('new-user');
await user.setData('email', 'john.doe@example.com');
return 'email corrected';
},
rollback: async () => {
const user = CSElement.getElementById('new-user');
await user.deleteData('email');
}
});
}
await transaction.commit();
console.log('✅ Транзакция успешно завершена');
} catch (error) {
await transaction.rollback();
console.error('❌ Транзакция отменена:', error);
}
```
### Управление точками сохранения
```typescript
class SavepointManager {
private transaction: Transaction;
private savepoints: Map<string, Savepoint> = new Map();
constructor(transaction: Transaction) {
this.transaction = transaction;
}
async createNamedSavepoint(name: string): Promise<Savepoint> {
const savepoint = await this.transaction.createSavepoint(name);
this.savepoints.set(name, savepoint);
return savepoint;
}
async rollbackToNamed(name: string): Promise<void> {
const savepoint = this.savepoints.get(name);
if (!savepoint) {
throw new Error(`Точка сохранения '${name}' не найдена`);
}
await this.transaction.rollbackToSavepoint(savepoint.id);
// Удаляем все точки сохранения после данной
for (const [spName, sp] of this.savepoints) {
if (sp.timestamp > savepoint.timestamp) {
this.savepoints.delete(spName);
}
}
}
async releaseNamed(name: string): Promise<void> {
const savepoint = this.savepoints.get(name);
if (savepoint) {
await this.transaction.releaseSavepoint(savepoint.id);
this.savepoints.delete(name);
}
}
listSavepoints(): Array<{ name: string; timestamp: number }> {
return Array.from(this.savepoints.entries()).map(([name, sp]) => ({
name,
timestamp: sp.timestamp
}));
}
}
```
## Обнаружение взаимоблокировок
```typescript
class DeadlockDetector {
private lockManager: LockManagerImpl;
private detectionInterval: NodeJS.Timeout;
constructor(lockManager: LockManagerImpl, intervalMs: number = 5000) {
this.lockManager = lockManager;
this.detectionInterval = setInterval(() => {
this.detectAndResolveDeadlocks();
}, intervalMs);
}
private async detectAndResolveDeadlocks(): Promise<void> {
const deadlocks = this.lockManager.detectDeadlocks();
if (deadlocks.length > 0) {
console.warn(`🚨 Обнаружено ${deadlocks.length} взаимоблокировок`);
for (const deadlock of deadlocks) {
await this.resolveDeadlock(deadlock);
}
}
}
private async resolveDeadlock(deadlock: any): Promise<void> {
// Стратегия: отменяем транзакцию с наименьшим приоритетом
const transactionsInDeadlock = deadlock.transactions;
// Сортируем по времени начала (младшие транзакции имеют приоритет)
transactionsInDeadlock.sort((a: any, b: any) => b.startTime - a.startTime);
const victimTransaction = transactionsInDeadlock[0];
console.log(`🎯 Выбрана жертва взаимоблокировки: ${victimTransaction.id}`);
// Принудительно откатываем транзакцию
try {
await victimTransaction.rollback();
console.log(`✅ Транзакция ${victimTransaction.id} отменена для разрешения взаимоблокировки`);
} catch (error) {
console.error(`❌ Ошибка отмены транзакции ${victimTransaction.id}:`, error);
}
}
destroy(): void {
if (this.detectionInterval) {
clearInterval(this.detectionInterval);
}
}
}
```
## Полный пример: Банковская система
```typescript
class BankingSystem {
private transactionManager: TransactionManagerImpl;
private lockManager: LockManagerImpl;
private deadlockDetector: DeadlockDetector;
private accounts: Map<string, CSElement> = new Map();
constructor() {
this.lockManager = new LockManagerImpl();
this.transactionManager = new TransactionManagerImpl(this.lockManager);
this.deadlockDetector = new DeadlockDetector(this.lockManager);
}
async createAccount(accountId: string, initialBalance: number): Promise<void> {
const account = new CSElement(accountId);
await account.setData('balance', initialBalance);
await account.setData('created', new Date().toISOString());
await account.setData('transactions', []);
this.accounts.set(accountId, account);
console.log(`🏦 Счет ${accountId} создан с балансом ${initialBalance}`);
}
async transfer(
fromAccountId: string,
toAccountId: string,
amount: number,
description: string = 'Перевод'
): Promise<string> {
return await this.transactionManager.withTransaction(async (tx) => {
const transferId = `transfer-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
// Операция 1: Проверка и списание средств
await tx.addOperation({
id: `${transferId}-debit`,
type: TransactionOperationType.UPDATE,
elementId: fromAccountId,
execute: async () => {
const fromAccount = this.accounts.get(fromAccountId);
if (!fromAccount) {
throw new Error(`Счет ${fromAccountId} не найден`);
}
const balance = await fromAccount.getData('balance') as number;
if (balance < amount) {
throw new Error(`Недостаточно средств. Баланс: ${balance}, требуется: ${amount}`);
}
await fromAccount.setData('balance', balance - amount);
// Добавляем запись о транзакции
const transactions = await fromAccount.getData('transactions') as any[];
transactions.push({
id: transferId,
type: 'debit',
amount: -amount,
description,
timestamp: new Date().toISOString(),
relatedAccount: toAccountId
});
await fromAccount.setData('transactions', transactions);
return balance - amount;
},
rollback: async () => {
const fromAccount = this.accounts.get(fromAccountId);
if (fromAccount) {
const balance = await fromAccount.getData('balance') as number;
await fromAccount.setData('balance', balance + amount);
// Удаляем запись о транзакции
const transactions = await fromAccount.getData('transactions') as any[];
const filteredTransactions = transactions.filter(t => t.id !== transferId);
await fromAccount.setData('transactions', filteredTransactions);
}
}
});
// Точка сохранения после списания
const debitSavepoint = await tx.createSavepoint('after-debit');
// Операция 2: Зачисление средств
await tx.addOperation({
id: `${transferId}-credit`,
type: TransactionOperationType.UPDATE,
elementId: toAccountId,
execute: async () => {
const toAccount = this.accounts.get(toAccountId);
if (!toAccount) {
throw new Error(`Счет ${toAccountId} не найден`);
}
const balance = await toAccount.getData('balance') as number;
await toAccount.setData('balance', balance + amount);
// Добавляем запись о транзакции
const transactions = await toAccount.getData('transactions') as any[];
transactions.push({
id: transferId,
type: 'credit',
amount: amount,
description,
timestamp: new Date().toISOString(),
relatedAccount: fromAccountId
});
await toAccount.setData('transactions', transactions);
return balance + amount;
},
rollback: async () => {
const toAccount = this.accounts.get(toAccountId);
if (toAccount) {
const balance = await toAccount.getData('balance') as number;
await toAccount.setData('balance', balance - amount);
// Удаляем запись о транзакции
const transactions = await toAccount.getData('transactions') as any[];
const filteredTransactions = transactions.filter(t => t.id !== transferId);
await toAccount.setData('transactions', filteredTransactions);
}
}
});
console.log(`💸 Перевод ${amount} с ${fromAccountId} на ${toAccountId} выполнен`);
return transferId;
}, {
isolationLevel: IsolationLevel.SERIALIZABLE,
timeout: 30000,
enableLogging: true
});
}
async getBalance(accountId: string): Promise<number> {
const account = this.accounts.get(accountId);
if (!account) {
throw new Error(`Счет ${accountId} не найден`);
}
return await account.getData('balance') as number;
}
async getTransactionHistory(accountId: string): Promise<any[]> {
const account = this.accounts.get(accountId);
if (!account) {
throw new Error(`Счет ${accountId} не найден`);
}
return await account.getData('transactions') as any[];
}
async getSystemStats(): Promise<any> {
const lockStats = this.lockManager.getStats();
const transactionStats = this.transactionManager.getStats();
return {
accounts: this.accounts.size,
locks: lockStats,
transactions: transactionStats,
totalBalance: Array.from(this.accounts.values()).reduce(async (sum, account) => {
const balance = await account.getData('balance') as number;
return await sum + balance;
}, Promise.resolve(0))
};
}
destroy(): void {
this.deadlockDetector.destroy();
this.transactionManager.destroy();
this.lockManager.destroy();
}
}
// Демонстрация использования
async function demonstrateBankingSystem() {
const bank = new BankingSystem();
try {
// Создаем счета
await bank.createAccount('acc-001', 1000);
await bank.createAccount('acc-002', 500);
await bank.createAccount('acc-003', 750);
console.log('📊 Начальные балансы:');
console.log('acc-001:', await bank.getBalance('acc-001'));
console.log('acc-002:', await bank.getBalance('acc-002'));
console.log('acc-003:', await bank.getBalance('acc-003'));
// Выполняем переводы
const transfer1 = await bank.transfer('acc-001', 'acc-002', 200, 'Оплата услуг');
const transfer2 = await bank.transfer('acc-002', 'acc-003', 150, 'Возврат долга');
console.log('💰 Переводы выполнены:', transfer1, transfer2);
console.log('📊 Конечные балансы:');
console.log('acc-001:', await bank.getBalance('acc-001'));
console.log('acc-002:', await bank.getBalance('acc-002'));
console.log('acc-003:', await bank.getBalance('acc-003'));
// История транзакций
console.log('📋 История acc-001:', await bank.getTransactionHistory('acc-001'));
// Статистика системы
console.log('📈 Статистика системы:', await bank.getSystemStats());
} catch (error) {
console.error('❌ Ошибка в банковской системе:', error);
} finally {
bank.destroy();
}
}
demonstrateBankingSystem().catch(console.error);
```
## 🎯 Заключение
Система транзакций и блокировок CSElement обеспечивает:
- **🔒 ACID-свойства** - атомарность, согласованность, изоляция, долговечность
- **⚡ Производительность** - оптимизированное управление блокировками
- **🛡️ Надёжность** - обнаружение и разрешение взаимоблокировок
- **🎯 Гибкость** - различные уровни изоляции и точки сохранения
- **📊 Мониторинг** - подробная статистика и логирование
---
**Следующий раздел:** [Персистентность и адаптеры →](10-persistence-and-adapters.md)