У меня есть партия на лиде, запуск которой запланирован каждые 5 минут.

Критерием для выбора записи в пакетной программе является поле в лид-экс. Flag = True.

Скажем, самый первый запуск партии и 50 тыс. записей с Flag = True. (размер пакета по умолчанию 200)

Первый пакет занимает более 5 минут из-за обработки, и, наконец, флаг будет обновлен до False. Так как первая партия еще не закончилась и запускается другая партия в течение следующих 5 минут. Следующая партия или вторая партия снова выберет 50 000 или в пределах 50 000 записей. Из-за этого я получаю ошибку блокировки записи.

Без изменения времени планирования и объединения пакетов я хочу избежать исключения блокировки записи. Есть ли способ избежать ошибки блокировки записи?

Возможным решением может быть цепочка партий (вызов метода «Партия в отделке»), но я ищу лучшие или другие решения.

2
Sarvesh 14 Апр 2020 в 17:31

2 ответа

Наше решение состояло в том, чтобы спроектировать то, что мы называем «адаптивным пакетом». Это более или менее обычные Batchable (расширяющие наш собственный абстрактный класс, который сам является Batchable), которые имеют встроенную цепочку, которая использует вызов System.scheduleBatch, чтобы гарантировать наличие 1-минутного промежутка между вызовами (чтобы чтобы избежать изнурительных асинхронных исполнений за день).

Эти адаптивные пакеты «выбрасываются» с помощью события платформы (гарантируя, что все они работают от имени пользователя автоматизированного процесса, поскольку у нас есть потребитель на основе триггера, который соответствует нашим потребностям), которые публикуются на шине событий, когда их состояние данных обнаружен (например, в потоке триггера или построителя процессов и т. д.). Этот вызов также предназначен для обеспечения того, чтобы только один экземпляр адаптивной пакетной реализации мог выполняться в любой момент времени. Это достигается двумя частями:

  1. Используя заданное имя задания для System.scheduleBatch (вы не можете запланировать более одного пакета одновременно с заданным именем) и
  2. Проверив асинхронные задания Apex, чтобы увидеть, существует ли уже запущенный экземпляр.

Последнее может быть выполнено в качестве предварительного условия перед планированием новой партии, хотя также должно быть выполнено в методе запуска Batchable, чтобы избежать условий гонки; вам гарантируется, что в любой момент времени в вашей организации вызывается только один метод пакетного запуска (одна из немногих точек «синхронизации» на платформе).

Если возникает новое событие платформы (и обрабатывается потребителем на основе триггера), мы проводим эти тесты, и если они проходят, у нас есть «адаптивный пакет», готовый к работе. Поскольку может существовать только один экземпляр, не стоит беспокоиться о конфликте, который вы видите. Если новые записи соответствуют критериям после запуска пакета, они обрабатываются нами, определяя (с запросом COUNT), что существуют записи, соответствующие условию данных, когда вызывается завершение. Если здесь есть совпадающие записи (ненулевое количество), мы снова планируем пакет (тем самым связывая выполнение пакета с небольшой паузой).

Преимущества этого подхода:

  • Никогда не беспокойтесь об условиях гонки, когда два экземпляра пакета конкурируют с одними и теми же данными.
  • Сократите количество запусков пакетов, поскольку они фактически выполняются только тогда, когда мы знаем, что есть какие-то данные для обработки (вместо запуска пакета по строгому расписанию).
  • Для нас убедитесь, что пакет выполняется для пользователя, отличного от того, который вызвал выполнение условий данных.

(В нашей структуре также реализованы некоторые «оптимизации задержки», поэтому мы не всегда делаем 1-минутную паузу перед запуском пакета, а просто гарантируем, что предыдущее выполнение было как минимум за 1 минуту до этого. Это позволяет выполнять немедленную обработку, когда данные редко соответствуют условиям данных.)

Вы можете прочитать немного больше о некоторых из этих моментов в этот ответ и и это тоже.

4
Phil W 15 Апр 2020 в 00:28

Вы должны сделать так, чтобы ваш метод execute повторно запрашивал записи, чтобы убедиться, что они не устарели и по-прежнему соответствуют критериям.

public void execute(Database.BatchableContext context, List<SObject> records)
{
    List<SObject> exclusiveAccess = [
        SELECT ...
        FROM MyObject__c
        WHERE MyFlag__c = true
        AND Id IN :records
        FOR UPDATE
    ];
    // remaining logic should operate on this query result
}

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

3
Adrian Larson 14 Апр 2020 в 17:36