Сообщество программистов Autodesk в СНГ

ADN Club => AutoCAD .NET API => Тема начата: Андрей Никифоров от 05-01-2025, 17:45:11

Название: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Андрей Никифоров от 05-01-2025, 17:45:11
Добрый день, коллеги!

Теоретический вопрос, прошу прояснить ситуацию - если кто может дать ссылку или сам проводил эксперименты на эту тему.
Вопрос такой: какова лучшая практика в использовании транзакции - сколько операций рекомендуется проводить в рамках одной транзакции, как лучше поступать - все запихивать в одну транзакцию, или последовательно открывать/закрывать несколько транзакций, в рамках каждой из них выполняя однотипные операции.

В качестве примера могу привести вот такую задачу: мне нужно в активном чертеже на каждом листе (Layout) создать область печати, соответствующую по размерам объекта (Entity), который находится на этом листе, если его внешняя геометрия (GeometricExtents) соответствует размерам стандартных форматов листов (А4, А3 и т.д.)
Что я делаю:
1. Читаю словарь листов, получаю указатели на все листы, перебираю их, открываю каждый лист на чтение и получаю их свойства.
2. Перебираю все объекты чертежа, открываю их на чтение, и если их геометрия соответствует требованиям, для каждого листа  сохраняю координаты будущей области печати.
3. Снова перебираю все листы, на этот раз открываю их на запись, и устанавливаю область печати по заранее вычисленным координатам.

При таком раскладе, я могу:
1. Открыть одну транзакцию и все три операции сделать в ее рамках.
2. Для каждой группы операций открывать/закрывать свою транзакцию, т.о. у меня будет три последовательных транзакции.
3. Открыть/закрыть транзакцию для первой операции, для второй операции - открывать/закрывать отдельную транзакцию для обработки объектов на каждом листе (т.е. транзакций будет столько, сколько листов в чертеже), и для третьей операции открыть/закрыть отдельную транзакцию.

Вопрос - как лучше делать, знаю, что можно сделать и так и так, но каковы рекомендации Autodesk по данному вопросу?
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: avc от 05-01-2025, 22:46:37
Про такие рекомендации я не слышал. Практически имеет смысл делать в одной транзакции большие быстрые циклы. Открытие транзакции не очень много времени съедает и в отдельных "одноразовых" процедурах вполне может быть своя транзакция. Так методы красивей получаются и будет удобнее передавать список безопасных ObjectId, а не опасные DBObject. Но есть конечно нюансы.
Замечено нестабильное поведение Автокад на работе с тысячами BRep солидов (фаталит). Вроде бы чутка по стабильней получается, если разбить транзакцию на части, по несколько сотен солидов. Но это из области танцев с бубном.  Вот если на отдельные команды бить, то точно заметно меньше фаталов.
Многие действия (в том числе работу с менеджером листов) требуется делать СТРОГО при закрытых всех транзакциях. Так что хочешь, не хочешь, а начинается свистопляска с кучей отдельных транзакций.
Для вашего примера не вижу разницы. Оформляйте так, чтоб был чище код.

Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Андрей Никифоров от 06-01-2025, 09:34:28
Спасибо за мнение. Уже становится яснее. Например, если
передавать список безопасных ObjectId, а не опасные DBObject
то ясно, что метод, куда передаем идентификаторы, должен открывать собственную транзакцию (то есть к этому времени все открытые транзакции были закрыты). Или нет? Можно же методу и открытыю транзакцию передать, вместе с ObjectId? Насколько тогда будет "опасной" такая техника?
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: avc от 06-01-2025, 10:38:54
Я когда-то в коротких "быстрых" функциях пытался использовать TopTransaction. Но при поиске глюков в обработчиках событий, я вообще не понимают - что это за транзакция? Моя или Автокад создал? Когда будет закрыта? И будет ли, или почему-нибудь будет откат? Когда я понял, что никак это все не контролирую, я переписал весь код и теперь передаю МОЮ транзакцию во все методы. Она бывает нужна даже если на входе DBObject. И еще рабочую базу данных часто передаю - с ней тоже много нюансов. Если напрягают методы с большим количеством аргументов, то можно класс-менеджер написать, который хранит транзакцию и базу данных.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Андрей Никифоров от 06-01-2025, 11:51:17
теперь передаю МОЮ транзакцию во все методы
Да, я тоже почти всегда так делаю. В том числе и поэтому возник вопрос, который я задаю в теме - правильно ли таскать одну транзакцию из метода в метод, или лучше открывать в каждом методе свою для небольшой порции операций. И вообще - сколько операций может "вместить" одна транзакция. Я так понимаю, четкого ответа нет, каждый делает, как умеет...
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: avc от 06-01-2025, 11:59:07
Совершенно точно, что нет ограничений на количество объектов в одной транзакции. Я спокойно пробегаюсь в одной транзакции по всей модели в огромных чертежах - ни проблем, ни дополнительных томозов. Комит транзакции похоже вообще ничего не делает (это совсем не те транзакции, что в нормальных базах данных). А откат конечно будет долгим, если очень много редактировать в одной транзакции. Но никаких проблем, связанных именно с количеством объектов или количеством изменений я лично не замечал. Интересно, конечно, что скажут эксперты..
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: alz от 06-01-2025, 23:38:24
Комит транзакции похоже вообще ничего не делает
Вот с этим не согласен, есть у меня одна прога, которая рекурсивно по блокам проходится кое что меняет и другие действия, на чертеже в 60 метров она пыхтела минут 10-20, причем на коммит уходило до полутора минут.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Привалов Дмитрий от 07-01-2025, 00:20:04
Вопрос такой: какова лучшая практика в использовании транзакции - сколько операций рекомендуется проводить в рамках одной транзакции, как лучше поступать - все запихивать в одну транзакцию, или последовательно открывать/закрывать несколько транзакций, в рамках каждой из них выполняя однотипные операции.

1. В документации вроде была рекомендация закрывать транзакцию сразу по использованию, т.е. не держать долго открытой.
2. В твоем случае должно хватить одной транзакции.
3. Лучше использовать одну транзакцию чем несколько, это быстрее, т.к. закрытие, открытие транзакции требует времени.
4. Не всегда возможно обойтись одной транзакцией. Редко, бывают случае когда нужно добавить сложный объект, например блок, таблицу, стиль и т.д. и закрыть транзакцию, чтобы объект появился в чертеже. Затем снова открыть транзакцию и произвести действия, что-то добавить, изменить. Иначе будут ошибки и вылеты в какой-нибудь версии автокада. Это нигде не описано.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Lemieux от 10-01-2025, 20:27:27
Не всегда возможно обойтись одной транзакцией. Редко, бывают случае когда нужно добавить сложный объект, например блок, таблицу, стиль и т.д. и закрыть транзакцию, чтобы объект появился в чертеже. Затем снова открыть транзакцию и произвести действия, что-то добавить, изменить. Иначе будут ошибки и вылеты в какой-нибудь версии автокада. Это нигде не описано.
Чтобы объект появлялся на чертеже транзакцию необязательно закрывать.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Александр Ривилис от 10-01-2025, 21:24:03
Lemieux,
Поясни свою мысль.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: avc от 10-01-2025, 21:33:01
Чтобы объект появлялся на чертеже транзакцию необязательно закрывать.
Я как раз о том же писал, что Commit ничего не делает, все изменения применяются ко всей БД сразу же, до Commit и видны из всех остальных транзакций и иногда видны даже пользователем (что полностью не соответствует понятию транзакция в реляционных базах данных). Никакой изоляции. Просто удобный способ замены using... ну еще откат полезен бывает, да...
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Александр Ривилис от 10-01-2025, 21:52:20
Я как раз о том же писал, что Commit ничего не делает,
Commit равносилен выполнению операции Close для всех объектов, участвующих в транзакции. Поэтому сказать, что он ничего не делает - неправильно.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: avc от 10-01-2025, 22:30:19
Commit равносилен выполнению операции Close для всех объектов
А что фактически делает операция Close ? Какие-то манипуляции с неуправляемой памятью? Все свойства всех объектов чертежа уже реально применены ДО закрытия транзакции.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Александр Ривилис от 10-01-2025, 22:43:23
Какие-то манипуляции с неуправляемой памятью?
Ну в принципе да. И кроме того срабатывают реакции AutoCAD на закрытие/изменения объектов.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Андрей Никифоров от 11-01-2025, 07:37:14
Честно говоря, я задавал этот вопрос в контексте конкретной задачи, поскольку изначально думал, что что-то не так делаю с транзакцией. Несмотря на все мои усилия, до сих пор не добился результата, прошу по возможности ответить - что я не так делаю. Задумка такая: на входе имею список полных названий файлов *.dwg, в цикле читаю базу данных каждого файла (без открытия документа), в каждой базе читаю список листов (layout), открываю каждый лист на чтение и читаю его свойства - имя (LayoutName), порядок следования (TabOrder) и размер области печати (PlotWindowArea). Все это сохраняю в отдельные списки и завершаю на этом работу с файлами. При всех этих операциях при отладке все нормально, но как только выходим за конечные скобки цикла перебора файлов VisualStudio выдает отдельное окно примерно с таким сообщением: acdbmgd.dll пытается совершить запись в область памяти, защищенную от записи. AutoCAD в этот момент фаталит. Иногда этого не происходит (сразу), но при закрытии чертежа, который был открыт при запуске этой команды, AutoCAD все равно падает, т.е. ошибка может проявиться либо сразу после завершения работы с внешними файлами, либо потом.

Вот код:
               
Код - C# [Выбрать]
  1. // список имен файлов
  2.                 List<string> FileNames = new List<string>();
  3.                 // здесь как-то получаем их имена...
  4.                 // пробегаемся по всем файлам
  5.                 foreach (string sFileName in FileNames)
  6.                 {
  7.                     // создаем пустую базу данных
  8.                     using (Database dbCurrent = new Database(false, true) as Database)
  9.                     {
  10.                         dbCurrent.ReadDwgFile(sFileName, FileOpenMode.OpenForReadAndAllShare, true, string.Empty);
  11.                         dbCurrent.CloseInput(true);
  12.                        
  13.                         //стартуем транзакцию
  14.                         using (Transaction tActiveTransaction = dbCurrent.TransactionManager.StartTransaction())
  15.                         {
  16.                             // получаем коллекцию объектов из словаря LayoutDictionaryId
  17.                             DBDictionary dbdLayouts = (DBDictionary)tActiveTransaction.GetObject(dbCurrent.LayoutDictionaryId, OpenMode.ForRead);
  18.                             // определяем e-numerator для этой коллекции
  19.                             DbDictionaryEnumerator enLayout = dbdLayouts.GetEnumerator();
  20.                             // пробегаемся по всем членам коллекции
  21.                             while (enLayout.MoveNext())
  22.                             {
  23.                                 // получаем указатель на лист
  24.                                 ObjectId idCurrentLayout = enLayout.Current.Value;
  25.                                 Layout layCurrent = (Layout)tActiveTransaction.GetObject(idCurrentLayout, OpenMode.ForRead);
  26.                                 // если это не модель - читаем ее свойства
  27.                                 if (layCurrent.ModelType == false)
  28.                                 {
  29.                                     // имя
  30.                                     string LayoutName = layCurrent.LayoutName;
  31.                                     int TabOrder = layCurrent.TabOrder;
  32.                                     Extents2d extPlotArea = layCurrent.PlotWindowArea;
  33.                                 }
  34.                             }
  35.                             tActiveTransaction.Commit();
  36.                         }
  37.                     }
  38.                 }// при достижении этой строки AutoCAD падает, либо потом, когда закрывается документ, из которого запускалась команда
               
Можете подсказать, что я не так делаю?

Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Lemieux от 11-01-2025, 08:21:26
Lemieux,
Поясни свою мысль.
Так Вы сами давно подсказали как работать с объектами, созданными во время транзакции.
Код - C# [Выбрать]
  1. document.TransactionManager.QueueForGraphicsFlush();
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: alz от 11-01-2025, 10:23:09
Критичного ничего не вижу, единственное в подобных сценариях я dbCurrent.CloseInput(true); не использую,  к тому же ты говоришь что происходит это уже считай за пределами выложенного куска, так что тут немного не понятно, что же там дальше. Ну как вариант если кад 2025 то возможно дело в попытке чтения открытого файла, раньше помню была у меня прога, проходила по файлам, редактировала, и сохраняла через try catch, естественно на открытых файлах вылетало исключение и прога переходила к следующему.
До 2025 все было норм, что-то изменило, список исключений выдавался - типа не удалось изменить. В 2025 все измененные файлы, если встретился хоть один, выкинувший исключение потребовали исправления при попытке открытия в каде, типа файл поврежден.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Андрей Никифоров от 11-01-2025, 10:36:28
как вариант если кад 2025
Тестирую на AutoCAD 2020
это уже считай за пределами выложенного куска, так что тут немного не понятно, что же там дальше
Если закомментировать все, что дальше идёт, все равно вылетает, т.е. я, видимо, как-то неправильно обращаюсь в этом фрагменте или с файлами, или с памятью. При отладке получаю сообщение, что acdbmgd.dll пытается записать информацию в секцию, защищённую от записи. Т.е. у меня есть подозрение, что я после всего держу какой-то фрагмент занятым, и при попытке его освобождения и происходит падение.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: alz от 11-01-2025, 11:16:12
Ну, я для себя вывел правило, проверять файл на наличие рядом .dwl файла, если есть даже не пытаться считывать, если требуется изменить, или делать копию куда нить в temp и читать уже спокойно копию.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: alz от 11-01-2025, 11:17:53
Если закомментировать все, что дальше идёт, все равно вылетает, т.е. я, видимо, как-то неправильно обращаюсь в этом фрагменте или с файлами, или с памятью. При отладке получаю сообщение, что acdbmgd.dll пытается записать информацию в секцию, защищённую от записи. Т.е. у меня есть подозрение, что я после всего держу какой-то фрагмент занятым, и при попытке его освобождения и происходит падение.

ну и как вариант все же попробовать закомментировать строку  dbCurrent.CloseInput(true); всегда работаю без нее и ни разу таких проблем не встречал.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Андрей Никифоров от 11-01-2025, 13:54:35
попробовать закомментировать строку  dbCurrent.CloseInput(true); всегда работаю без нее и ни разу таких проблем не встречал
Есть одна древняя статья, где чел очень убедительно описывает, зачем нужно использовать CloseInput(). Вот ссылка на статью: https://adndevblog.typepad.com/autocad/2012/07/using-readdwgfile-with-net-attachxref-or-objectarx-acdbattachxref.html
Вкратце: приводит две причины - чтобы быть уверенным, что внешний файл прочитался полностью и не произошло кэширование (лично для меня не очень понятно), и второе - CloseInput() "отпускает" внешний файл, и после этого его можно перезаписать. Вот тут как раз все ясно.
Кроме того, видел множество примеров с чтением базы данных из внешнего файла, и в очень многих из них CloseInput() вызывается без объяснений, как само собо разумеющееся.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Андрей Никифоров от 12-01-2025, 08:44:39
Так как ошибка проявлялась стабильно (AutoCAD валился каждый раз), то решил действовать исключением - сначала закомментировал весь код, а затем начал постепенно опять добавлять. Оказалось, что если просто перебирать файлы и читать их базу данных, то ошибка не появляется. Она начинает появляться, как только я начинаю использовать DbDictionaryEnumerator. Я решил немного переделать код - выбросить enumerator и перебирать элементы словаря через DBDictionaryEntry. И с таким способом ошибка пока не появляется. Буду еще тестировать, но есть надежда, что проблема решена. Вот как выглядит конечный результат, который стабильно работает:

Код - C# [Выбрать]
  1.                 // список имен файлов
  2.                 List<string> FileNames = new List<string>();
  3.                 // здесь как-то получаем их имена...
  4.                 // пробегаемся по всем файлам
  5.                 foreach (string sFileName in FileNames)
  6.                 {
  7.                     // создаем пустую базу данных
  8.                     using (Database dbCurrent = new Database(false, true) as Database)
  9.                     {
  10.                         dbCurrent.ReadDwgFile(sFileName, FileOpenMode.OpenForReadAndAllShare, true, string.Empty);
  11.                         dbCurrent.CloseInput(true);
  12.                        
  13.                         //стартуем транзакцию
  14.                         using (Transaction tActiveTransaction = dbCurrent.TransactionManager.StartTransaction())
  15.                         {
  16.                             // получаем коллекцию объектов из словаря LayoutDictionaryId
  17.                             DBDictionary dbdLayouts = (DBDictionary)tActiveTransaction.GetObject(dbCurrent.LayoutDictionaryId, OpenMode.ForRead);
  18.                             // перебор элементов словаря вот таким способом устраняет ошибку
  19.                             foreach (DBDictionaryEntry dicEntry in dbdLayouts)
  20.                             {
  21.                                 Layout layCurrent = (Layout)tActiveTransaction.GetObject(dicEntry.Value, OpenMode.ForRead);
  22.                                 if (layCurrent.ModelType == false)
  23.                                 {
  24.                                     // имя
  25.                                     string LayoutName = layCurrent.LayoutName;
  26.                                     int TabOrder = layCurrent.TabOrder;
  27.                                     Extents2d extPlotArea = layCurrent.PlotWindowArea;
  28.                                 }
  29.                             }
  30.                             tActiveTransaction.Commit();
  31.                         }
  32.                     }
  33.                 }

Когда я создавал тему, то думал, что что-то не так делаю с транзакцией - меня смущало, что я работаю не с текущим документом, а вовсе без документа, читая базы данных сторонних документов. Почитал всякие статьи, где описывается, как получать свойства объектов без открытия транзакции, вместо обычной транзакции использовать OpenCloseTransaction - все это я перепробовал и ничего не помогало - по завершении моей команды AutoCAD все равно опрокидывался. Теперь я вижу, что причина такого поведения была, скорее всего в другом.

Если кто-то может предположить - почему происходила ошибка при использовании DbDictionaryEnumerator - прошу прокомментировать.
Название: Re: Сколько операций рекомендуется выполнять в рамках одной транзакции?
Отправлено: Lemieux от 12-01-2025, 09:32:56
Открыть все объекты ForWrite  ;D