Присказка
Я давно тружусь "прикладным программистом для системы 1С:Предприятие". С точки зрения маркетинга и своевременности на рынке очень удачная система и, что характерно, в целом старается оставаться. С технической/технологической - не безупречно, но глядя на остальные какашки отечественного рынка, просто гордость за 1С берёт.
Хорошая система, универсальная, да вот есть и у неё области для которых она не пригодна. Использовать её в этих областях можно, но это будет дорого и ненадёжно. Вот возьмём, например, высоконагруженные системы: биллинг, он-лайн трейдинг на бирже, системы с большим количеством пользователей и операций пользователей. Ситуация чем-то напоминает "культ карго". Вроде и СУБД приличные, кросс-платформенность, что аж обзавидоваться, объектная модель относительно стройная (кто не согласен - могу аргументированно посравнивать с Аксессом тем же), диаграмки бизнес-процессов появились, внедренцы нынче не в трамвае в футболке приезжают, а на иномарках в пинжачках. Красота. А вот уй. Не предназначена. Программисты-разработчики её не писали для такого, и уши этих решений будут постоянно торчать.
Вот тут обычно из слушателей-собеседников выкристаллизовываются 2 большие группы. Одни скажут: "Вот ведь говнецо ваша 1С, там даже язык программирования на русском", другие: "Да кто ты такой так осквернять святыню. На 1С можно написать искусственный разум, и это уже сделал Сергей Нуралиев, но пока не пришло время явить Его миру". Вот с этими группами я прямо здесь могу распрощаться. Первые уходят искать достойного конкурента 1С, вторые идут искать информацию о том как 1С в оперативной памяти числа хранит, и сравнивают с другими реализациями. А я отмечу лишь несколько моментов, что мне мешает в создании высоконагруженных систем в 1С. Перечислю далеко не всё, скорее то, обо что реально спотыкаемся. Есть куча мелких проблем, но они скорее жужжат, но не кусают. Типа таких: неполное использование возможностей СУБД (простите, кросплатформенность), прожорливость по ресурсам (покупайте железку, на лицензиях сэкономили), медленный интерпретатор (куда торопиться - один фиг на выборке из БД всё просадится), куча блокировок при работе с БД, невозможность создавать произвольные индексы и еще куча обглоданных и сто раз описанных проблем. К тому же это и не проблемы, а так - чтобы жизнь интереснее была.
- Реально выбивает отсутствие типа времени, точнее, чем секунда. Не зря 1С - 1 с. Для бухгалтерской программы в большинстве случаев откровенно пофигу на доли секунд, но если у вас больше 100000 событий в день, то их даже нельзя раскидать на оси времени в достоверной последовательности не прибегая к костылям. Что делать если система должна хранить информацию о более чем миллионе событий (документов) в день, да еще и не раскиданные равномерно по суткам - решительно не понятно.
- Очень напрягает медленная работа с БД на изменение. По сути update'ов и insert'ов работающих с множеством записей вообще нет, а delete только для очень частного случая (и то - кто из программистов 1С не сносил регистр сведений целиком из-за этого случая? :)). Скорость вставки, если не прибегать к мегаизвратам для записей в 100-200 байт не выходит за 500-1000 записей в секунду из одного клиентского соединения. Для большинства типовых загрузок это вообще 1-50 документов в секунду. Те же данные банальным bulk insert вставляются 10000-30000 записей/сек на той же железке.
- Бедный язык запросов. Он похож на обычный SQL, но в нём нет нескольких важных особенностей. Нет windowed аггрегатных функций, нет рекурсивных запросов, нет подзапросов для выборки одиночных значений в части между SELECT и FROM (как нет и OUTER APPLY, которым это можно заменить), нет банального EXISTS.
- Самое важное. 1С писали не для этого. Продеменстрирую на паре примеров.
На новый 2011 год мы перешли с древней, как говно мамонта версии 8.0.12 (дадада, той самой, в которой еще не было администраторов сервера), на 8.1.15. Релиз 8.1.15 является последним и наиболее стабильным релизом версии 1С 8.1. Последующие были уже 8.2, да и по стабильности там пока еще вопросов многовато (и это за год, когда система уже якобы вовсе и не бета!). В целом переход был гладким (насколько он может быть гладким для совершенно нетиповой базы нашего размера). Переходили максимально аккуратно и поэтому большинство решений использовавшиеся в 8.0 продолжают (временно) использоваться в 8.1, т.е. наш код был к концу декабря так, чтобы он одинаково работал в 1С 8.0 и 1С 8.1. Полученную информационную систему конечно тестировали (и функционально, и нагрузочно, и пользовательские части и серверные), но видимо недостаточно. Уже в первые рабочие дни мы словили 2 мощные проблемы.
сказка
Глюк первый. При нормальной работе пользователй сервер приложения вдруг отправляется в мир иной, оставив клиентские сессии сиротинушками, а незафиксированные транзакции откатывает, служба рестартует. После второго "Ой" от сервера я настроил технологический журнал на сервере и уже на третий за день инцидент выяснилось, что падает при получении списка активных польователей (а надо сказать, что это действие у нас происходило ОЧЕНЬ часто). Ну что, к вечеру 11.01 с кучей матов был создан костыль, который максимально исключал получение этого списка. 12.01 вечером выяснилось, что этого недостаточно. 13.01 были убраны все получения списка программные и всем, кто имел доступ к получению списка интерактивно было устно запрещено его получать. В пятницу я подготовил скриптец, который получает этот список в цикле, но не смог его тестировать даже на разработческих серверах (уж очень весело всё падало). Дожили до выходных. Начали гонять тесты (на тестовых и боевой системе), выяснили:
- Падает на активной работе служебных пользователей (которые часто подключаются/отключаются)
- Падает на основном сервере и на разработческом сервере, но не падает на старом разработческом сервере. На серверах подготовки отчетов проверить было нельзя, поэтому там не проверяли.
Копаем дальше. Подозреваем что всё дело в кривой установке (у остальных же не падает) или битности приложения (есть 2 версии сервера 32 и 64 бит). Вызываем духа админов. Не помогает. Тратим время на эти проверки. Не помогает. Танцуем с бубном. Не помогает. Пытаемся найти того служебного пользователя, во время которого падает сервер. Выясняется, что во время работы любого служебного пользователя 1С может упасть, а обычные пользователи на это не влияют. Еще примерно через час выясняем, что проблема в том, что служебные пользователи прекращают сеанс вызовом метода "ПрекратитьРаботуСистемы()". Вообще у 1С есть 2 метода для завершения сеанса: "ЗавершитьРаботуСистемы()" и "ПрекратитьРаботуСистемы()". Первый боле "мягкий", а второй - безусловный. Смоделировали. Да, именно так: в некоторых случаях когда в одном клиентском сеансе выполняется "ПрекратитьРаботуСистемы()", а в другом выполняется "ПолучитьСоединенияИнформационнойБазы()" система падает. А на старом сервере она не падала, потому что он слишком медленный (недостаточная интенсивность операций). Поменяли везде "ПрекратитьРаботуСистемы()" на "ЗавершитьРаботуСистемы()" и живём теперь. А служебных пользователей активно переносим в серверные фоновые и регламентные задания (т.е. теперь будет не вызов специальным клиентом действий, а сразу на сервере действия).
Но это была разминка. Сама ошибка достаточно легко локализовалась, данные целы, выявляется и исправляется проблема быстро (все падают, переподключаются и всё). А вот вторая проблемка до сих пор не факт что решена (хоть и не происходила уже 2 дня). Проблема такая: иногда, без видимых причин один из клиентов (опять же обычно служебных, мать их, пользователей) говорит: 3902! "Тhe COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION." Дело осложняется следующими нюансами: программист 1С не напрямую работает с СУБД и такую ошибку вызвать не может (платформа ему сама насчитает коммиты и выдаст своё сообщение, которое хотя бы отловить в коде не проблема), сеанс клиента тупо висит с модальным диалоговым окном об ошибке и эту ошибку не отловить в коде (учитывая, что это периодически запускаемое задание - крайне обидно и сложно отловить). Но и это не самое печальное. Печально то, что в технологическом журнале сервера и клиента 1С (это грубо говоря полный лог действий системы) нет очевидных причин и одинаковых ситуаций для падения. И самое противное, что Менделеев всё-таки был прав. Ежели где-то коммитов излишки, то, блин где-то их будет недостача. И эта недостача была в одном из соседних коннектов.
С точки зрения целостности данных ситуация ужасная:
- В том соединении где была ошибка - фиг знает были ли команды DML (изменения данных), которые должны быть в транзакции одиночными. Или хотя бы все ли они были вне транзакции и выполнены.
- Во стором соединении данные откатываются. Это бы не было страшным, если бы система не считала эту транзакцию закоммиченной! Пользователь (или робот) честно думал, что транзакция зафиксирована и послал, например, уведомление в другую систему о загрузке данных. А потом его труды пропадают.
- Пока второе соединение висит "без коммита" у него открыта транзакцияи скапливается дикое количество блокировок.
Пипец! Пришлось настроить оповещение от MS SQL об ошибке 3902 на СМС. При возниконовении ошибки приходится останавливать всю работу системы, проверять что делалось в отменённых соединениях (по ТЖ - технологическому журналу и по трассе событий SQL:Batch Complete). Читать ТЖ достаточно проблемно. Это набор текстовых файлов, каждый из которых отражает работу одного процесса за один час и даже не в самом полном режиме у нас занимает 1-3 ГБ. 1-3 ГБ за час. Парсера у нас пока нет (он входит в диагностический пакет 1С, котрый будет куплен, наверное в марте). Формат такой, что в SQL его запихнуть проблематично. Большинство редакторов текста с такими файлами не дружат. В этой проблеме приходится разбираться на фоне других шерховатостей перехода и на фоне вполне понянтного недовольства начальства и заказчиков.
Выяснилось, что проблема возникает так: при большом количестве коротких транзакций иногда бывает что одно соединение с БД из пула выдаётся двум клиентским соединениям. Одно уже к этому времени сделало BEGIN TRAN, а второе только запрашивает соединение. При этом у первому соединению отдаётся новое соединение. Порог при котором это всё начинает себя так вести - около 1000 транзакций в секунду с одного соединения.
У нас такое количество транзакций происходит при чтении изменений из плана обмена "ВыборкаИзменений = УзелПланаОбмена.ВыбратьИзменения();" и потом "Объект = ВыборкаИзменений.Получить();" в цикле. При этом каждое ВыборкаИзменений.Получить() является отдельной транзакцией. Ну плюс на самом деле мы там несколько небольших прикладных ошибок нашли, но факт остаётся. Залечили достаточно просто: каждые N ВыборкаИзменений.Получить() и операций выгрузки данных делаются в единой транзакции, так что количество транзакций резко снизилось (фактически в 2*N раз). Уже несколько дней я не получаю (тьфу-тьфу) страшную СМС. Потихоньку отслеживаем все корявые места в коде, где может быть много транзакций и пытаемся это дело оптимизировать. На выходных попробую репру для 8.2 сделать. Если будет валить 8.2 - пошлю в 1С. Если не будет (первую багу, кста, исправили или уменьшили вероятность до ненаблюдаемой) - придётся смириться, потому что на 8.2 мы раньше июне не мигрируем.
Мораль басни
Помню, как-то раз я удивился, правильно ответив (не угадав!) Денису Анатоличу (Анатолич, привет!) на пост типа "угадайте где бага". Прочитал пост в сортире с телефона, ответил с телефона. С++ (о котором был вопрос), конечно, немного знаю, но не зубр (хотя небольшие внешние компоненты и не проблема написать). Потом на самом деле оглянулся, что ему в чем-то проще. У него хотя бы исходный текст его мамонта под рукой есть (ну или был на тот момент). У меня только ибучая интуиция - где же программисты платформы чаще всего ошибки совершают. Люди вообще и программисты в частности делают очень много ошибок. Иногда мне кажется, что программисты вообще делают только ошибки, а работающий код - результат какого-то нематериального процесса, в котором программисты лишь выступают мутагенным фактором. Так вот этой мутагенности в программистах платформы явно выше в тех областях, которые качаются больших нагрузок. Поэтому если будете делать высоконагруженную систему на 1С - скорректируйте бюджет на пару зарплат таких решателей проблем как я (не-не-не, не почасовка, оклад, и не одного как я, а двоих), не забывая, что эта зарплата будет на некоторый процент выше большинства остальных разработчиков. Прибавьте риски (простой системы и потеря данных). Что? Многовато? Ну это по сравнению с чем. Если у вас есть готовый движок для биллинговых систем, то да, а если вы думаете, что ваши программисты могут написать лучше, чем 1С, то сомневаюсь (если конечно у вас нет хотя бы пары десятков упомянутых Анатоличей, но, как гововил товарищ Сухов: "Это вряд ли"), а если думаете, что лучше да еще и дешевле, то срочно бросайте те наркотики, которые употребляете.