# 🔒 Транзакции и блокировки

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) 