2016-09-03 3 views
0

Мы отправляем большое количество писем через пакетный процесс. Этот процесс выполняется внутри CFThread. Каждый набор записей извлекается из SQL Server. Когда мы просматриваем электронные письма, мы обновляем записи как отправленные.CFThread пакетный процесс - отчеты обрабатываются несколько раз

Когда мы запускаем процесс каждую минуту, он отлично работает. При запуске каждые 30 секунд он начинает обработку записей несколько раз.

Хотя мы отмечаем, что записи «вытащили», по-прежнему сталкиваются с проблемами. Похоже, что пока первый запрос все еще обрабатывается, начинается второй запрос. Второй запрос извлекает записи до того, как первый запрос может обозначить их как «вытащил». Таким образом, одни и те же записи обрабатываются дважды.

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

База данных: (псевдокод)

BEGIN 
DECLARE @MyTempTable table(
ID INT IDENTITY(1,1) NOT NULL, 
emailBody varchar(max), 
eMailfromdisplay varchar(200), 
eMailFromAdr varchar(200), 
eMailsubject varchar(200), 
emailAdr varchar(200), 
FirstName_vch varchar(250), 
LastName_vch varchar(250), 
Sent INT, 
pulled INT, 
masterEmailTableID BIGINT 
); 


insert into @MyTempTable 
select Distinct top 1000 
    emailBody, 
    eMailfromdisplay, 
    eMailFromAdr, 
    eMailsubject, 
    emailAdr, 
    FirstName, 
    LastName, 
    Sent, 
    pulled , 
    masterEmailTableID 
from 
    emailMasteTable 

where 
    pulled = 0 and 
    Sent = 0 



SELECT TOP 1000 id 
    ,masterEmailTableID 
    ,Email_adr 
    ,firstName 
    ,LastName 
    ,Sent 
    ,pulled 
FROM @MyTempTable where sent = 0 and pulled = 0  



    -- Other tables status updates 

update emailMasteTable set pulled = 1 where EmailID IN (Select distinct masterEmailTableID from @MyTempTable); 

END 

ColdFusion: (псевдокод)

<cfquery name="getMessages"> 
    exec spGetEmailMessages 
</cfquery> 


<cfoutput query="getMessages"> 

    <cfmail> 
    <!--- Email Stuff ---> 


     <cfquery> 
      update emailMasteTable set sent = 1 where EmailID = #getMessages.masterEmailTableID#; 
     </cfquery> 
    </cfmail> 
</cfoutput> 
+1

Я думаю Невозможно сказать, не видя фактического сценария, можете ли вы поделиться им, если это возможно. – Susang

+2

Если несколько потоков могут потенциально получать доступ к записям, недостаточно их отмечать как завершенные * после *, они обрабатываются. Вы ДОЛЖНЫ реализовать какой-то механизм блокировки для предотвращения условий гонки. Например, вы можете реализовать несколько статусов: новый, обработать, завершить, не удалось. Проверьте записи со статусом new и отметьте их * при их извлечении *. Это препятствует тому, чтобы какой-либо другой процесс извлекал одни и те же данные. По завершении обработки отметьте их как завершенные/неудачные, как обычно. – Leigh

+0

* хотя мы отмечаем записи, когда мы тянем как потянутые, но все еще сталкивающиеся с проблемами * Этого не произойдет, если все будет сделано правильно. Вам нужно опубликовать код. В частности, эта часть логики. – Leigh

ответ

0

Я рекомендовал бы читать на how locking works in SQL Server. Это большая тема, но вкратце, просто обертывание операторов в хранимой процедуре не означает, что доступ является каким-то однопоточным или что другие потоки магически не позволяют захватить один и тот же набор записей. Ничто в текущем коде не позволяет одной и той же серии записей «читать» несколько раз до того, как UPDATE начинает работать. Фактически, чем больше потоков задействовано, но и частый интервал, тем больше вероятность того, что несколько потоков будут обрабатывать одинаковые записей.

Недостаточно отметить партию записей как «в использовании» после они извлекаются. Они должны быть отмечены , когда они извлекаются. Например, запустите UPDATE, чтобы отметить партию записей. Используйте OUTPUT clause для захвата идентификаторов и сохранения их в какой-то временной таблице. Затем присоединитесь к таблице temp для выполнения любых дополнительных операций. Учитывая, что это часто доступная таблица, вы должны выполнить профилирование, чтобы определить оптимальный/наиболее эффективный подход.

-- Stores next batch of ids 
DECLARE @TempTable TABLE (masterEmailTableID BIGINT); 

-- Flag next batch as being "processed" 
-- Note: Without an order by clause result order is arbitrary 
UPDATE TOP (1000) BigTable 
SET Pulled = 1 
OUTPUT inserted.masterEmailTableID INTO @TempTable 
WHERE Pulled = 0 
AND Sent = 0 

-- do other queries .... 

-- Return details for current batch 
SELECT main.masterEmailTableID 
     , main.masterEmailTableID 
     , ... other columns 
FROM BigTable main INNER JOIN @TempTable tmp ON tmp.masterEmailTableID = main.masterEmailTableID 
GO 

Примечание стороны, вы можете также рассмотреть вопрос о добавлении уникального номера партии к столу, чтобы облегчить массовые обновления статуса, а не обновлять элементы по отдельности.

+0

Спасибо! @Leigh, что вы предположили, что это не 100% моего решения, но это дало мне хорошее направление, что делать ... вы отлично:) ... –

+0

Из любопытства, что еще вам нужно было сделать? Блокировка и захват обновленного идентификатора одновременно должны быть все, что вам нужно для пакетной обработки. – Leigh

+0

Hi @Leigh, извините за задержку. Im выбирает записи -> обновление сначала -> выбор только обновленных записей. до сих пор работающий хорошо. –