Синхронизация грузила сервер четыре ночи подряд, программист не виноват

18.09.26

Интеграция - Перенос данных 1C

В коде синхронизации стоял комментарий: операция безопасна, если документ уже на месте, она ничего не делает. Комментарий был правдив и обошёлся дороже любой ошибки. Каждую ночь код безусловно дёргал перемещение около 1400 раз, и на каждый вызов принимающая сторона пересчитывала структуру коллекции в 139 килобайт, в том же потоке, которым отвечала на запросы. Формально ноль изменений, фактически плотный поток тяжёлых записей. Внутри: почему проверка живости показывала здоровье, пока запись деградировала до десятков секунд; чем "сервис занят" отличается от "сервис недоступен", если смотреть снаружи; как три повторные попытки чуть не наплодили дублей; и что осталось необъяснённым, включая 688 против 696 в отчёте прогона. Идемпотентность описывает результат. Про цену вызова она не говорит ничего.

Ночная синхронизация падала по превышению времени ожидания записи. Четыре ночи подряд, каждая хуже предыдущей. На четвёртую сервис лёг целиком: хост отвечает на пинг, порт соединяется за три миллисекунды, а по протоколу приложения ответа нет вообще.

В коде, который всё это устроил, стоял комментарий: безопасная операция без последствий.

Комментарий не врал по смыслу и врал по цене. Операция действительно ничего не меняла - и стоила столько, что укладывала принимающую сторону. Разбор ниже про то, как безусловное "ничего не делать" превращается в самоперегрузку, и почему одна сторона тут виновата не больше другой.

Чтение здорово, запись мертва

Первые три ночи всё выглядело странно: сервис явно нездоров, а любая проверка показывает норму.

Причина в том, что проверяли не то. Чтение отвечало за 40-130 миллисекунд с кодом 200. Любая проверка доступности, хоть автоматическая, хоть руками из браузера, отвечала как положено.

Запись при этом занимала 24 секунды, потом больше тридцати. А клиент имел жёсткое ограничение в 30 секунд без повторных попыток.

Отсюда картина "непонятно, то работает, то нет": половина операций укладывалась, половина нет, и от нагрузки зависело, какая именно.

Практический вывод. Проверка живости, которая делает только чтение, не доказывает работоспособности сервиса. Если приложение пишет - проверка обязана писать, хотя бы в служебный объект. Читающая проверка при записи, уехавшей в десятки секунд, покажет вам полное здоровье.

Второе, что вылезло тут же и обошлось дороже всего: пять баз обрабатывались в одном блоке перехвата ошибок. Падение одной уносило обработку остальных четырёх, хотя с ними всё было в порядке. Одна больная база превращалась в четыре потерянные ночи работы.

Четвёртая ночь: сервис перестал отвечать

К четвёртой ночи деградация дошла до конца.

Сеть в порядке: хост пингуется, соединение транспортного уровня (TCP, Transmission Control Protocol) устанавливается за три миллисекунды. А запрос по протоколу приложения висит без ответа.

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

Различать эти два состояния стоит уметь: "сервис недоступен" и "сервис занят" лечатся по-разному, а снаружи выглядят одинаково.

Проверяется это без специальных инструментов. Соединение на порт устанавливается - процесс жив и слушает. Запрос по протоколу висит дольше обычного времени ответа в разы - процесс не успевает разбирать очередь. Если же соединение не устанавливается вовсе, разговор про приложение можно не начинать: смотреть надо сеть, правила фильтрации и то, запущен ли процесс.

Наш код и комментарий про безопасную операцию

Теперь причина, и она на нашей стороне.

Синхронизация после каждого обновления документа безусловно вызывала операцию перемещения документа в нужный раздел - для каждого документа коллекции, независимо от того, находился он уже там или нет.

Рядом стоял комментарий, что операция безопасна и ничего не делает, если документ уже на месте.

Комментарий был технически верен. Результат операции не менялся - документ, уже лежащий в нужном разделе, оставался там же.

Масштаб: коллекция из 1 417 документов, около 1 400 вызовов за прогон. Каждую ночь, независимо от того, изменилось хоть что-нибудь или нет.

Почему комментарий врал

Здесь и находится главный урок статьи.

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

"Ничего не меняет" читается как "ничего не делает". Между тем сервис на каждый такой вызов синхронно прогонял три своих обработчика и пересчитывал структуру коллекции, а она весила 139 килобайт. То есть тысяча четыреста раз за ночь принимающая сторона перестраивала одно и то же дерево и записывала один и тот же результат.

Здесь стоит остановиться на слове "синхронно". Дорогая работа сама по себе ещё терпима: она отъедает процессор, прогон идёт дольше, все живут. Смертельной её делает то, что она выполняется в том же потоке, который обязан отвечать на входящие запросы. Пока идёт пересчёт, отвечать некому, и для любого клиента сервис в этот момент отсутствует. Тысяча четыреста таких окон подряд складываются в непрерывное отсутствие.

Формально ноль изменений. Фактически - плотный поток тяжёлых операций записи, которые сервис обязан выполнить, потому что откуда ему знать, что результат не изменится.

Правило, которое стоит забрать: идемпотентность описывает результат, а не цену. Прежде чем вызывать операцию безусловно, надо ответить, во что обходится вызов, который ничего не изменит. Если ответ "столько же, сколько настоящий" - вызывать безусловно нельзя.

Проверка на своей стороне при этом почти бесплатна: сравнить текущее положение с целевым и вызывать только при расхождении. Одно условие.

Усилитель на принимающей стороне

Виноват не только вызывающий код, и вот вторая половина картины.

Принимающий сервис работал в один процесс: веб-интерфейс, фоновый обработчик и механизм совместного редактирования - всё в одном цикле обработки событий.

При такой конфигурации тяжёлая фоновая работа не просто конкурирует с интерфейсом за ресурсы, она блокирует его целиком. Процесс крутился на ста процентах в пользовательском режиме, и обработать входящее соединение было некому. Отсюда и висящие без ответа запросы при живом порте.

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

Отклонённая версия

На ревью высказали подозрение: возможно, испортилась карта соответствия документов, и синхронизация гоняет данные впустую именно из-за неё.

Версия правдоподобная и проверяемая. Сделали сплошную сверку: 696 записей карты из 696 сопоставлены с фактическим состоянием. Ноль устаревших, ноль отсутствующих.

Карта в полном порядке, версия закрыта. Проверка заняла меньше времени, чем занял бы спор о ней.

Что сделали с обеих сторон

Лечение оказалось несоразмерно простым по сравнению с масштабом отказа.

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

На стороне сервиса - снять ограничение в один процесс. Приложение пошло в пять процессов, и фоновая работа перестала блокировать обслуживание запросов.

На своей стороне - одно условие перед вызовом: сравнить, где документ лежит сейчас, с тем, где он должен лежать, и вызывать перемещение только при расхождении.

Плюс подняли ограничение времени ожидания записи до 90 секунд и добавили три повторные попытки вместо одной попытки без повторов.

Повторные попытки чуть не создали новую аварию

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

Создание документа на таймауте неидемпотентно. Сервер успел применить запись, ответ до нас не доехал, клиент считает попытку неудачной и повторяет. В итоге в приёмнике два одинаковых документа, и второй мы создали собственными руками, страхуясь от сбоя.

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

Закрыли это пробой перед повтором: перед тем как повторять создание, идём и проверяем, не появился ли документ с таким заголовком. Если появился, повтор превращается в чтение и запись не отправляется.

Общее правило отсюда простое. Повторные попытки можно вешать только на операции, у которых повтор ничего не ломает. На запись, которая создаёт сущность, повтор без предварительной проверки состояния вешать нельзя, и таймаут тут опаснее явной ошибки: явная ошибка хотя бы честно говорит, что сервер запрос отверг.

И вот тут честная оговорка, которую надо проговорить вслух. Позже наблюдалась запись длительностью 77 секунд при поднятом ограничении 90. Запас составил 17 процентов. Это подобрано впритык: любая новая нагрузка на сервис, и мы снова упрёмся в ту же стену. Правильно было бы разобраться, почему запись вообще занимает больше минуты. Порог мы подняли вместо этого.

Код ответа врёт в обе стороны

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

Пакетная архивация напечатала в журнал FAILED на все 35 документов. Пошли смотреть, что не заархивировалось, и обнаружили, что заархивированы все 35, в правильном порядке, с шагом примерно в полсекунды на вызов. Мутация применилась полностью, а ответ пришёл ошибочный.

Обратная ошибка живёт в том же коде: обёртка над вызовами возвращала пустой словарь на любой ответ, отличный от 200, и не поднимала исключение. Вызывающий код видел "пусто" и шёл дальше. Тихий провал, который в журнале выглядит как штатная работа.

На встроенном языке тот же глушитель выглядит как пустой блок Исключение вокруг вызова: ошибка проглочена, вызывающий код едет дальше, в журнале штатная работа. Чтением такое место не находится, зато находится поиском по коду. У меня для этого Анализ кода внешних обработок 1С: пустой перехват и запись объекта внутри цикла там отдельные правила, каждая находка с номером строки. Конфигурацию он не читает, только .epf и .erf, зато самописные обёртки над обменом у меня жили как раз в них.

Отсюда правило, которым теперь пользуюсь: код ответа сообщает про попытку и ничего не сообщает про состояние объекта. Когда операция важная, состояние надо перечитать. И журнал, в котором написано FAILED, стоит проверять так же придирчиво, как журнал, в котором написано OK: ошибочный FAILED заставляет чинить то, что уже работает, и это дорогое занятие.

Ещё один пример из той же серии. Архивация возвращала 403, и первая версия была "сервисному пользователю не хватает прав". Прав хватало, токен был администраторский. 403 приходил на попытку заархивировать уже заархивированное: по смыслу это "поздно, уже сделано", хотя код говорит про запрет. Гипотезу про права мы проверили и отбросили, а на реальную причину вышли, посмотрев фактическое состояние документа. Порядок действий работает почти всегда: сначала смотрим состояние объекта, потом лезем в права.

Как выкатывали фикс

Отдельная история, стоившая суток.

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

Вторые грабли ровно там же. Пересоздание контейнера, то есть среды, в которой поднят сам сервис, во время активного прогона убивает прогон и оставляет после себя замок, который потом снимаешь вручную. То есть сама выкатка фикса создаёт следующий инцидент, если делать её в окно, когда задание работает.

Вывод не про конкретный инструмент. Если у вас ночное задание, то выкладка исправлений в это задание обязана иметь своё окно, а факт доезда изменений на прод надо проверять отдельно от факта "сборка зелёная". Иначе диагностика идёт по коду, которого на проде нет.

Контрольный прогон

Результат после правок:

Показатель Значение
Баз обработано 5 из 5
Код возврата 0
Время прогона 344 секунды
Перемещений документов 0 (было около 1 400)
Пропущено без изменений 680
Обновлено 5
Создано новых 3

Следующий прогон - 215 секунд, ноль созданных, ноль обновлённых, 682 пропущено. Сервис отвечает кодом 200 за 47 миллисекунд при фоновой загрузке процессора 77-89 процентов.

Обратите внимание на последнюю цифру: загрузка процессора осталась высокой, а отвечать сервис стал за 47 миллисекунд. Это и есть доказательство того, что дело было в блокирующем характере нагрузки. Объём тут вторичен.

Счётчики между собой не сходятся, и я это не спрятал

Здесь надо сказать неудобное, пока читатель не сложил числа сам.

Карта соответствия ведёт 696 записей. В контрольном прогоне 680 пропущено, 5 обновлено, 3 создано, в сумме 688. Восемь записей не объясняются ничем: они не попали ни в одну из категорий отчёта. На следующем прогоне картина снова другая: 682 пропущено при нуле созданных и нуле обновлённых, то есть с 688 не сходится ещё на шесть.

Что это, я честно не знаю. Наиболее вероятная версия - документы, уходящие в другую ветку обработки и не попадающие в счётчики отчёта. Это именно версия: специально мы её не проверяли, потому что к моменту, когда числа сошлись в одну таблицу, авария была уже закрыта.

Отдельно про 1 417 и 696, чтобы не путались. 1 417 - это размер коллекции на принимающей стороне, по ней и шли безусловные перемещения. 696 - число записей в нашей карте соответствия. Это разные множества, и полного объяснения разрыва между ними у меня тоже нет.

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

Почему прогон без работы всё равно стоит три минуты

Прогон, в котором ноль перемещений, занимает 215 секунд вместо 344. Разница в 129 секунд приходится на восемь записей контрольного прогона, три создания и пять обновлений, если поделить - около 16 секунд на запись. Порядок ровно тот, ради которого поднимали порог ожидания: запись на этом сервисе по-прежнему медленная, просто теперь её мало.

Всё остальное время уходит на обход коллекции. То есть даже после исправления синхронизация тратит основное время на проверку того, что работы нет.

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

Что осталось незакрытым

Хронический фон процессора объяснения не получил. До исправлений сервис держал около 83 процентов постоянно, и после устранения главной причины высокая фоновая загрузка никуда не делась - просто перестала мешать. Что её создаёт, в этом разборе не установлено.

Запас по времени ожидания мал. Семнадцать процентов - это не запас, это отсрочка.

Причина медленной записи не найдена. Мы обошли её повторами и увеличенным порогом, устранить не смогли.

Счётчики прогона не сходятся с картой соответствия. 688 против 696 в контрольном прогоне и ещё шесть расхождения на следующем. Версия есть, проверки нет.

Стоимость инцидента не посчитана. Сколько суток сервис был недоступен всем пользователям и скольких людей это задело, мы не замеряли. Видно только, что недоступность длилась не меньше двух суток. Поэтому история читается как техническая, хотя по-хорошему её надо было предъявлять бизнесу в часах простоя.

Пары "до и после" по главной метрике нет. Сколько занимал прогон до исправления, никто не мерил: до аварии никому не приходило в голову, что это интересная величина. Есть 344 и 215 секунд после, а с чем их сравнивать - неизвестно. Если вы читаете это перед похожим разбором, снимите текущее время прогона сейчас, пока всё работает. Замер, сделанный после аварии, доказывает вдвое меньше.

Открытый вопрос

Вопрос, на который у меня нет уверенного ответа. Комментарий "безопасно, ничего не делает" был написан честно и оказался ловушкой, потому что описывал результат и молчал про стоимость. У меня ощущение, что это очень распространённый класс комментариев - формально правдивых и вводящих в заблуждение. Кто-нибудь ввёл у себя правило писать в таких местах ещё и цену? Интересно, работает ли это на практике или превращается в ещё одно поле, которое никто не заполняет.

Другие наши инструменты:

  • Анализ нагрузки кластера 1С - показывает, кто именно держал базу и кто грузил сервер в конкретный час. Ровно тот вопрос, с которого начинается разбор ночного окна.
  • Журнал транзакций переполнен: кто держит и чем - вторая половина той же ночи. Обмен пишет пачками, журнал растёт, и место кончается раньше, чем заканчивается регламент.
  • Трансформатор SQL в 1С - переводит запрос из профайлера обратно в имена справочников и регистров. Нужен, когда видно, что база стоит, а чей это запрос непонятно.
  • Карта объёмов базы 1С - что в базе занимает место и как быстро растёт. Обмены обычно оставляют следы именно в регистрах, и видно это только по объёмам.

Вступайте в нашу телеграмм-группу Инфостарт

идемпотентность таймаут записи health-check проверка живости однопроцессный режим блокирующая нагрузка синхронизация документов самоперегрузка обработка исключений повторные попытки диагностика недоступности сервиса

Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

См. также

Перенос данных 1C Программист 1С:Предприятие 8 1С:Управление производственным предприятием 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Платные (руб)

Перенос документов, начальных остатков и справочной информации из УПП 1.3 в ERP 2 | из УПП 1.3 в УТ 11 | из УПП в КА 2 | Правила конвертации (КД 2) | Более 360 предприятий выполнили переход с использованием этого продукта! | Сэкономьте время - используйте готовое решение для перехода! | Позволяет перенести из УПП 1.3 в ERP / УТ 11 / КА 2 всю возможную информацию | В переносе есть фильтр по организации и множество других опциональных параметров выгрузки | Есть несколько алгоритмов выгрузки остатков на выбор

58000 руб.

04.08.2015    193266    467    309    

466

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист 1С:Предприятие 8 1С:Комплексная автоматизация 1.х 1С:Управление производственным предприятием 1С:Бухгалтерия 3.0 Россия Бухгалтерский учет Платные (руб)

Перенос данных из 1С:Управление производственным предприятием 1.3 в 1С:Бухгалтерия предприятия 3.0 с помощью правил обмена | Можно выполнить переход с УПП на БП 3 или запускать выгрузку данных за выбранный период времени | Переносятся документы, начальные остатки и вся справочная информация | Есть фильтр по организации и множество других параметров выгрузки | Поддерживается несколько сценариев работы: как первичный полный перенос, так и перенос только новых документов | Перенос данных возможен в "1С: Бухгалтерия 3.0" версии ПРОФ, КОРП или базовую | Переход с "1С: УПП1.3" / "1С:КА 1.1" на "1С:БП3.0" с помощью правил конвертации будет максимально комфортным! | Можно бесплатно проверить перенос на вашем сервере!

50050 руб.

25.02.2015    191418    375    295    

430

SALE! 15%

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист 1С:Предприятие 8 1С:Розница 2 1С:Управление нашей фирмой 1.6 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 1С:Розница 3.0 Россия Платные (руб)

Правила в универсальном формате обмена для ERP 2.5, КА 2.5, УТ 11.5, БП 3.0, Розница, УНФ, для последних версий конфигураций. Ссылки на другие конфигурации в описании публикации. Правила совместимы со всеми другими версиями конфигураций новыми и старыми, поддерживающими обмен и синхронизацию в формате EnterpriseData. Не требуется синхронного обновления правил после обновления другой конфигурации, участвующей в обмене. Типовой обмен через планы обмена кнопкой Синхронизация вручную или автоматически по расписанию, или вручную обработкой.

27633 руб.

12.06.2017    163766    994    329    

487

SALE! 10%

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист 1С:Предприятие 8 1С:Управление производственным предприятием 1С:Бухгалтерия 3.0 Россия Бухгалтерский учет Управленческий учет Платные (руб)

Переносите справочную информацию, остатки и документы из УПП 1.3 в Бухгалтерию 3.0 с помощью готовых правил. Переносится более 50 видов документов. Простой интерфейс и понятные настройки.

42000 37800 руб.

15.12.2021    36167    265    68    

202

Перенос данных 1C Файловый обмен (TXT, XML, DBF), FTP Программист 1С:Предприятие 8 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Платные (руб)

Перенос данных из ERP в БП 3 | из КА 2 в БП 3 | из УТ 11 в БП 3 | из ЕРП в БП 3 | Сэкономьте время - используйте готовое решение для перехода! | Перенос разработан в формате КД 2 (правила конвертации данных) | Переносятся все возможные виды документов, начальных остатков и нормативно-справочная информация| Можно опционально выгружать каждую пару "номенклатура+характеристика" как отдельную номенклатуру | Есть выгрузка настроек счетов учета и зарплатных данных из ERP / КА 2 | Можно проверить на вашем сервере перед покупкой

58000 руб.

15.04.2019    86671    232    182    

168

Перенос данных 1C Взаиморасчеты Оптовая торговля Логистика, склад и ТМЦ Файловый обмен (TXT, XML, DBF), FTP Системный администратор Программист 1С:Предприятие 8 1С:Управление торговлей 10 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Управленческий учет Платные (руб)

Можно проверить до покупки, оставьте заявку! Воспользовались более 268 компаний! Перенос данных из УТ 10.3 в УТ 11 | из УТ 10.3 в КА 2 | из УТ 10.3 в ERP. Решение для перехода с УТ 10.3. Можно перенести начальные остатки, нормативно-справочную информацию и все возможные документы. При выгрузке можно установить отбор по периоду, организациям и складам.

50200 руб.

24.04.2015    210036    180    253    

299

Файловый обмен (TXT, XML, DBF), FTP Перенос данных 1C Системный администратор Программист Бухгалтер 1С:Предприятие 8 1С:Бухгалтерия 3.0 Россия Платные (руб)

Обработка не только формирует начальные остатки по всем счетам на нужную дату (экономя время на свёртке базы БП 3), но и полностью переносит справочные данные и документы за заданный период. Гибкая настройка включает фильтр по организациям и множество параметров выгрузки. Работайте в удобном формате: выполните однократный полный переход или настройте регулярную догрузку только новых документов из БП 3 в БП 3.0. Интеграция правил конвертации в план обмена гарантирует точную выгрузку исключительно зарегистрированных объектов.

70760 руб.

10.04.2026    1211    3    8    

2

Рабочее место Производство готовой продукции (работ, услуг) Перенос данных 1C Пользователь 1С:Предприятие 8 1С:Управление производственным предприятием 1С:Документооборот 1С:Комплексная автоматизация 2.х 1С:КА 1С:ДО Платные (руб)

Продукт "Интеграция с 1С:Документооборот" позволяет использовать функции программы "1С:Документооборот 8" напрямую из учетной системы (1С:УПП; 1С:КА, 1С:УТ 10.3, 1С:БГУ 1.0, 1С:ЗБУ 1.0, 1С:УПП для Казахстана и отраслевых решений, разработанных на их основе) на платформе "1С:Предприятие 8": выполнять и ставить задачи, просматривать документы, скан-копии и прочие файлы, штрих-кодировать документы отправлять письма, вести учет рабочего времени - не входя в "1С:Документооборот 8", работая в одной программе, что значительно сокращает время и делает работу более комфортной и эффективной. Продукт прошел сертификацию 1С-Совместимо

135530 руб.

11.06.2015    63580    39    20    

51
Для отправки сообщения требуется регистрация/авторизация