Skip to content

fix(connector): сохранение с подчиненными таблицами выполняется монопольно - #162

Open
sfaqer wants to merge 2 commits into
nixel2007:masterfrom
sfaqer:claude/thread-safety-a2-exclusive-save
Open

sfaqer wants to merge 2 commits into
nixel2007:masterfrom
sfaqer:claude/thread-safety-a2-exclusive-save

Conversation

@sfaqer

@sfaqer sfaqer commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Часть разбиения #154 (2 из 4): монопольное сохранение сущности с подчинёнными таблицами.

Note

Строится поверх #161. SQL-реализация ВыполнитьМонопольно опирается на признак транзакции коннектора оттуда: если транзакция уже открыта, действие выполняется в ней. Пока тот PR не влит, здесь виден и его коммит 583445d. Собственный коммит этого PR — 8be7edb.

Что чинит

Сохранение сущности с подчинёнными таблицами — несколько запросов: строка сущности, удаление прежних строк коллекций, вставка новых. Они шли без изоляции.

Как проявлялось на master Исправление
Два параллельных сохранения одной сущности давали в коллекции строки обоих, а читатель видел её пустой или неполной; так было на InMemory, JSON, SQLite и PostgreSQL ВыполнитьМонопольно(Действие): SQL — своя транзакция (у SQLite BEGIN IMMEDIATE), внутри уже открытой — как есть; InMemory — под блокировкой записи базы; JSON — под блокировкой записи каталога (раньше блокировка была на файл)

Ломающее изменение (Major.Connector)

ВыполнитьМонопольно(Действие) — новый обязательный метод интерфейса АбстрактныйКоннектор. Коннектор, который не умеет изолировать, просто вызывает Действие.Выполнить(). Сторонний коннектор без метода отвергается при создании ИсточникДанных. Это та же смена второй цифры версии, что и ВТранзакции().

Что ещё меняется снаружи

  • Сохранение с подчинёнными таблицами вне транзакции наблюдатели видят как запросы BEGIN/COMMIT вокруг него.
  • JSON: записи в одном каталоге выполняются по одной, чтения по-прежнему идут параллельно.
  • Пользовательская НачатьТранзакцию у SQLite осталась отложенной. BEGIN IMMEDIATE пробовали поставить и туда, но тогда при общем кэше вторая параллельная транзакция, даже пустая, сразу падает с «database table is locked» (упали два теста МногопоточностьФоновыеЗадания). Ограничение — две отложенные транзакции «прочитал, потом записал» блокируют друг друга — описано в документации.
  • Запись сущности вынесена в internal-класс ЗаписьСущности. Монопольно выполняется только сохранение сущности с подчинёнными таблицами.

Тесты

  • ПараллельноеСохранениеКоллекций — InMemory, JSON, SQLite (файловая база), PostgreSQL; на master все красные. Для InMemory окно гонки расширяет фикстура НаблюдательПаузыПослеЗапроса.
  • ПараллельныеТранзакцииSQLite — два монопольных действия «прочитать, потом записать» на файловой базе; с отложенным BEGIN получалось «database is locked».
  • ИсточникДанных.КоннекторБезМонопольногоВыполненияОтвергается — проверен мутантом.

Полный прогон без PostgreSQL: OneScript 2.2.0 — 310 из 311, 2.3.0-next+a02323a1 — 310 из 311. Один пропущенный тест — ПараллельноеСохранениеКоллекцииPostgreSQL, его проверяет CI.

Все четыре части разбиения, слитые вместе, сливаются без конфликтов и совпадают с #154 (плюс тест из #157). Там же: 312 из 313 на обоих движках, пропущен тот же тест PostgreSQL.

🤖 Generated with Claude Code

sfaqer and others added 2 commits October 7, 2026 09:05
…атывается

Алгоритм ВыполнитьСКоннектором мог начать транзакцию методом коннектора или
запросом BEGIN и не завершить ее: она уходила с соединением к другому потоку,
и его COMMIT фиксировал строки алгоритма. SQL-коннектор теперь сам ведет
признак транзакции по выполненным запросам (BEGIN/START открывают,
COMMIT/END/ROLLBACK закрывают, ROLLBACK TO - нет), а пул по ВТранзакции()
откатывает брошенную транзакцию при возврате соединения.

ВТранзакции() - обязательный метод интерфейса АбстрактныйКоннектор: коннектор
без транзакций отвечает Ложь, сторонний коннектор без метода отвергается при
создании ИсточникДанных (смена Major.Connector).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ольно

Сохранение сущности с подчиненными таблицами - несколько запросов: строка
сущности, удаление прежних строк коллекций, вставка новых. Они шли без
изоляции: два параллельных сохранения одной сущности давали в коллекции строки
обоих, а читатель видел ее пустой или неполной (InMemory, JSON, SQLite,
PostgreSQL).

Библиотека теперь выполняет такое сохранение методом коннектора
ВыполнитьМонопольно(Действие), обязательным в интерфейсе АбстрактныйКоннектор:
- SQL - своя транзакция (у SQLite BEGIN IMMEDIATE), внутри уже открытой - как
  есть; пользовательская НачатьТранзакцию у SQLite осталась отложенной;
- InMemory - под блокировкой записи базы;
- JSON - под блокировкой записи каталога (раньше блокировка была на файл).

Сторонний коннектор без метода отвергается при создании ИсточникДанных (та же
смена Major.Connector, что и ВТранзакции).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 19 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: eb5330c8-a4c8-422f-a4e0-72e5e4b5ba3d
📥 Commits

Reviewing files that changed from the base of the PR and between 8241fc5 and 8be7edb.

📒 Files selected for processing (30)
  • README.md
  • docs/АбстрактныйКоннектор.md
  • docs/КоннекторJSON.md
  • docs/КоннекторSQLite.md
  • docs/ПотокобезопаснаяРаботаСБД.md
  • src/internal/Классы/ЗаписьСущности.os
  • src/internal/Классы/СоединениеСБД.os
  • src/internal/Модули/РаботаСКоннекторами.os
  • src/Классы/АбстрактныйКоннектор.os
  • src/Классы/АбстрактныйКоннекторSQL.os
  • src/Классы/КоннекторInMemory.os
  • src/Классы/КоннекторJSON.os
  • src/Классы/КоннекторPostgreSQL.os
  • src/Классы/КоннекторSQLite.os
  • tests/fixtures/КоннекторБезМонопольногоВыполнения.os
  • tests/fixtures/КоннекторБезНаблюдения.os
  • tests/fixtures/КоннекторБезПризнакаТранзакции.os
  • tests/fixtures/КоннекторНеполноНаблюдаемый.os
  • tests/fixtures/КоннекторСЛоманымОписанием.os
  • tests/fixtures/КоннекторСобытиемПриПодключении.os
  • tests/fixtures/КоннекторТранзакцийДляТестов.os
  • tests/fixtures/КоннекторУправляемоеОткрытие.os
  • tests/fixtures/НаблюдательПаузыПослеЗапроса.os
  • tests/fixtures/СущностьСКоллекцией.os
  • tests/fixtures/ЧтениеИЗаписьАвтора.os
  • tests/ДоступКоннектором.os
  • tests/ИсточникДанных.os
  • tests/КоннекторJSON.os
  • tests/ПараллельноеСохранениеКоллекций.os
  • tests/ПараллельныеТранзакцииSQLite.os
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant