25.08.2010, 16:01 | #1 |
Участник
|
Одновременная вставка записей, совпадающих по индексу.
Билд 4.0.2503.1208, но насколько понимаю, тут вопрос вне версионный.
Для начала опишу модель архитектуры. Есть некоторый ф-ционал(предположим, что примитивный логгер показателей системы во время некого события). Метод для вызова логера находится внутри некого метода, для простоты будем считать, что это некоторый апдейт. Что-то вроде: X++: ttsbegin; //Инициализация только для примера. Суть в том, что до вызова логера должна //отработать одна часть процесса someBuffer.SomeField = SomeNewValue; someBuffer.SomeField2 = SomeNewValue2; someBuffer.SomeField3 = SomeNewValue3; //вызов логера Loger.logit(); //Вторая часть процесса после логера + окончательный апдейт. someBuffer.update(); ttscommit; Так вот, в чем суть, если процесс, в который внедрен вызов логера запускается одновременно на двух клиентах(а такая ситуация на проекте, увы, встречается в реальной практике) - происходит следующее: вылетает ошибка о невозможности записи в таблицу логера, так как запись существует, но худшее то, что при этом прерывается процесс. Что я только не делал: - и запись в лог в Try-catch обрамлял, наивно думая, что в такой ситуации должна инициироваться исключительная ситуация UpdateConflict(Хотя так же перепробовал и UpdateConflictNotRecovered и просто Error и вообще кэтч блок без фильтра), но увы, catch блок ничего ловится. - и OCC на таблице логгера отключил(эфект частичный, на 2-3 тестах отрабатывает хорошо, на 4ом все-равно валится). В итоге, у меня появился лишь один сомнительный вариант: сделать индекс по дате, пироду и под-периоду логирования неуникальным и потом чистить повторяющиеся варианты. В общем, если есть у кого более интересные идеи - милости прошу.
__________________
Axapta has seduced me deadly! |
|
25.08.2010, 16:15 | #2 |
Ищущий знания...
|
на вскидку.
1. сделать индекс не уникальным. 2. в методе validateWrite() таблицы написать проверку на дубликат. 3. перед инсертом записи в таблицу вызывать validateWrite() (что кстати BP настоятельно реомендует делать и перед инсертом и перед апдейтом). т.е. в итоге в методе logit()должно появиться: X++: if (log.validateWrite())
log.insert();
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
25.08.2010, 16:24 | #3 |
Участник
|
lev, ну точно так же на вскидку могу предположить, что если метод будет запущен одновременно, то и validateWrite в обоих случаях вернет true, так как дубликата на тот момент ещё не будет в базе.
__________________
Axapta has seduced me deadly! |
|
25.08.2010, 16:28 | #4 |
Участник
|
Честно, не знаю есть ли в "четверке" Exception "DuplicateKeyException".
В "пятерке" именно это исключение "вываливается". |
|
25.08.2010, 16:30 | #5 |
Участник
|
5ке повезло!) В 4ке нет такого вида исключений. Мало того, просто catch без фильтра тоже ничего не ловит.
__________________
Axapta has seduced me deadly! |
|
25.08.2010, 16:35 | #6 |
Участник
|
Хм... в 3-ке генерится исключение "Error" ))
|
|
25.08.2010, 16:36 | #7 |
Участник
|
Исключение внутри блока ttsbegin/ttscommit передает управление на следующий оператор после ttscommit самого верхнего уровня.
Т.е. обработчик должен выглядеть так X++: try { ttsbegin; // что-то делаем ttscommit; } catch { // обрабатываем исключение }
__________________
Axapta v.3.0 sp5 kr2 |
|
|
За это сообщение автора поблагодарили: tricky (1), HorrR (1). |
25.08.2010, 16:37 | #8 |
Ищущий знания...
|
т.е. Вы хотите сказать, что параллельно выполняется один и тот же код строка в строку в двух процессах? очень слабо в это вериться...
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
25.08.2010, 16:43 | #9 |
Участник
|
Именно так. В определенное время на двух клиентах стартует один и тот же процесс. Не совсем пакетная отработка но практически.
__________________
Axapta has seduced me deadly! |
|
25.08.2010, 16:46 | #10 |
Участник
|
Предположу также, что может вводить в заблуждение выводимый инфолог.
В этом случае можно воспользоваться этим советом: Закрыть программно Infolog |
|
25.08.2010, 16:56 | #11 |
Ищущий знания...
|
ну тогда действительно тут только отлавливать событие catch-ем.
__________________
"Страх перед возможностью ошибки не должен отвращать нас от поисков истины." (с) С Уважением, Елизаров Артем |
|
25.08.2010, 16:57 | #12 |
----------------
|
а что, если запись в лог делать в отдельном коннекшене, может тогда при ошибке записи лога его можно будет там же и отловить?
возникнет другая проблема, что при ошибке в основном процессе лог останется. |
|
25.08.2010, 16:59 | #13 |
Участник
|
Цитата:
Сообщение от AndyD
Исключение внутри блока ttsbegin/ttscommit передает управление на следующий оператор после ttscommit самого верхнего уровня.
Т.е. обработчик должен выглядеть так X++: try { ttsbegin; // что-то делаем ttscommit; } catch { // обрабатываем исключение } У меня то сам метод логгера именно так был оформлен: X++: try { ttsbegin; // что-то делаем ttscommit; } catch { // обрабатываем исключение }
__________________
Axapta has seduced me deadly! |
|
25.08.2010, 17:00 | #14 |
Участник
|
насколько я понял нужна целостность уникального по ключу процесса, который может быть запущен на нескольких клиентах, при этом если по ключу процесс выполнен, то не запускать процесс на другом клиенте. Тогда:
1. проверять дубликат до запуска процесса - а где-же еще 2. первый клиент может не завершить транзакцию, тогда отказ второго клиента от процесса - ошибка - то есть надо подождать разрешения ситуации 3. нужен некий семафор реальный или виртуальный - имеет статусы - выполняется, завершен - это и есть информация о том, что происходит алгоритм вырисовывается такой - ключ - уникальный индекс 1. пока чужой семафор есть и статус = выполняется и время < тайм аута - ждем чем все кончится 2 если чужой семафор - завершено - выход 3. если нет чужого семафора - создать свой = выполняется 4. процесс до 5. лог 6. процесс после 7. семафор = завершено технически наиболее близкое решение - это код выделения номеров по номерным сериям - 3 пункт это и есть выделить номер (ключ), только я имею ввиду не создавать номерную серию, а взять блоки кода по транзакциям - при разноске, например, документов сам процесс разноски еще не закоммичен, а вот номер уже использован и закоммичен!!! и другие клиенты об этом знают, там же есть моменты на предмет разрулить ситуацию, когда два клиента почти одновременно выполнят 3 пункт. Если 6 пункт не достигнут - надо в catch сбросить свой семафор. семафором можно сделать саму таблицу логов - добавить статус или - ADOConnection - читать незакомиченные (установить в команде уровень изоляции) записи лога (семафоры) другого клиента - а что?! здесь нет минуса первого варианта, а именно - если выключить комп на пункте 4, то .... семафор придеться чистить ручками или ваять нечто. по любому главное - проверять процесс на запуск другим клиентом и ждать чем все там завершиться - придеться! Последний раз редактировалось titov; 25.08.2010 в 17:06. |
|
25.08.2010, 18:26 | #15 |
Developer
|
В аксапте в качестве семафора можно создать и использовать табличку, и вместо
X++: log.insert(); X++: select firstonly forupdate table where ...; log.insert(); table = null; |
|
26.08.2010, 10:04 | #16 |
MCTS
|
Зависит от того, как вы реализуете блокирование ресурсов вашей таблицы логов при вставке/обновлении/удалении записей в ней.
__________________
Dynamics AX Experience |
|
26.08.2010, 11:54 | #17 |
Administrator
|
Вообще-то основная задача лога - это фиксировать события. Т.е. если событие в логе не фиксируется - то это "караул". Т.е. делать уникальный индекс на таблице логов - заведомо некорректно. Лучше потом выборку построить по каким-то критериям.
Потом, любое логирование какого-то события заведомо замедляет обработку этого события, в связи с чем на логируемую табличку (в рамках ускорения) нелогично накладывать какие-либо индексы. Однако, я подозреваю, что тут не совсем логирование. Скорее всего тут - генерация осмысленных записей (используемых потом в рабочем процессе), которые логичным образом не должны дублироваться. В этом случае совет от titov как нельзя более лучшим образом подходит. Кстати - в качестве идеи - можно предложить подумать - а как же в АХ производится разноска журналов (из кода)? Два человека могут открыть ведь разнести документ? Ответ. При разноске (из кода) блокируется журнал, т.е. второй человек не может запустить разноску (из кода), если первый уже запустил ее. В интерфейсе журнал блокируется при открытии строк журнала. Наглядно в АХ работает процедура закрытия склада - в начале работы - в табличку InventClosing помещается запись о начале работы, а по завершению - статус записи обновляется.
__________________
Возможно сделать все. Вопрос времени |
|
Теги |
try/catch, исключения, как правильно, транзакции |
|
|