Конкурентные операции модификации

Конкурентные операции модификации - это операции модификации, с помощью которых можно модифицировать одну и ту же запись файла БД в нескольких транзакциях без возникновения конфликта блокировок.

Имеется следующие конкурентные операции модификации:

Каждая конкурент-модификация создает в памяти сервера псевдоэкземпляр записи, содержащий значения модифицируемых полей. CONCURRENTADD-модификация содержит значения-добавки к соответствующим полям опорной записи. CONCURRENTRESET-модификация предполагает, что каждое указанное в ней поле должно получить значение по умолчанию.

Конкурентные операции модификации могут выполняться только для файлов, находящихся в конкурентном режиме, иначе они выбрасывают исключение 962. Включить конкурентный режим можно с помощью TABLE.AccessDataMode.

В целом конкурентные операции ведут себя сходно:

  1. Подобно процедуре SEARCH, по указанному в параметрах индексу и значению полей индекса, выполняется поиск записи, подлежащей модификации.
    Если нужная запись нашлась, то ее экземпляр, соответствующий последней завершенной транзакции, называется опорным экземпляром для конкурентных операций. В момент завершения очередной транзакции появляется новый опорный экземпляр.
    Если нужная запись не нашлась, то считается, что конкурент-операция выполняет вставку новой записи (CONCURRENTADDONLY этого не делает). Тогда предполагается, что опорный экземпляр в полях индекса поиска имеет соответствующие значения полей, по которым выполнялся поиск, а остальные его поля заполняются default-значениями. А если файл имеет автоинкрементный индекс, первые поля которого в итоге получают непустые значения, а последнее поле - пустое, то значение последнего поля вычисляется по правилам автоинкрементного индекса.
  2. В памяти сервера формируется псевдоэкземпляр текущей записи, содержащий значения модифицируемых полей. В CONCURRENTADD-модификации это значения-добавки, в CONCURRENTRESET-модификации - значения по умолчанию.
  3. Затем конкурентные операции выполняют работу сходную с работой других процедур позиционирования: на опорный экземпляр найденной записи накладываются все выполненные конкурентные операции собственной транзакции. Полученный в итоге экземпляр, как результат выполнения операции, помещается в курсор файла. Т.о. и CONCURRENTADD, и SEARCH и все другие процедуры позиционирования учитывают все конкурентные модификации собственной транзакции, но не учитывают конкурентные модификации чужих транзакций, в том числе главных, если наша транзакция подчиненная.
  4. В момент завершения транзакции все ее конкурентные операции воздействуют на опорный экземпляр и модифицируют его, после чего он сохраняется на диске. Если происходит откат транзакции, конкурентные операции просто забываются, и на диске ничего не меняется.
  5. Поля, подверженные конкурентным ADD, не должны входить ни в один из индексов файла. В противном случае выбрасывается исключение 955. Поля CONCURRENTRESET-а могут входить в индексы, но не более чем по одному в каждый индекс.
  6. Логика аддитивных операций предполагает, что добавляемое значение должно добавляться к некоторому опорному значению. Когда CONCURRENTADD выполняет вставку новой записи, то опорными значениями аддитивных полей являются значения по умолчанию. Поэтому для таких полей значения по умолчанию должно быть непустыми. В противном случае выбрасывается исключение 954.
  7. Поскольку одноименные конкурентные модификации независимых транзакций не дают конфликт блокировок, а количество транзакции, одновременно модифицирующих запись, не ограничено,  и транзакции могут откатываться и завершаться в любой момент и в любом порядке, то значения полей, подверженных конкурентным модификациям, полученные из процедур позиционирования,
    1. показывают не полную картину, т.к. не учитывают конкурентные модификации чужих транзакций
    2. устаревают прямо в момент их получения, т.к. в любой момент одна из чужих транзакций может завершиться, в результате чего изменится опорный экземпляр записи, а значит и результат операции позиционирования нашей транзакции, т.е. два подряд вызова SEARCH одной и той же позиции могут дать разные результаты.
    Первая проблема частично решается применением функции CONCURRENTGET. Она учитывает все конкурентные модификации собственной и ее главных транзакций, все CONCURRENTRESET-ы чужих транзакций, а CONCURRENTADD-ы чужих транзакций учитывает так, чтобы для указанного в ее параметрах поля вернуть в зависимости от значения параметра CalcMethod минимальное или максимальное значение на момент ее вызова. Т.е. если, например, опорный экземпляр имеет значение 10, наша транзакция выполнила CONCURRENTADD со значением 2, некая независимая транзакция - со значением 7, и еще одна независимая - со значением -5, то SEARCH даст результат 12, т.е. 10+2, CONCURRENTGET с CalcMethod=CONCURRENTGET_MAX даст 19, т.е.10+2+7, а CONCURRENTGET с CalcMethod=CONCURRENTGET_MIN даст 7, т.е.10+2-5.