Динамическое обновление 1С — обновление конфигурации базы данных без выгона пользователей: монопольный режим не нужен, новую конфигурацию получают только новые сеансы. Если процесс оборвался — упал конфигуратор, перезагрузили сервер, пропала сеть, — получаем ошибку динамического обновления 1С: конфигуратор требует завершить операцию, а 1С не запускается после обновления конфигурации ни у кого.
Ниже — что ломается, штатное восстановление, приём автора сайта на уровне SQL, безопасный порядок обновления и код 1С для оповещения о перезапуске. Имена условные: сервер srv01, база trade.
Основную конфигурацию правит конфигуратор, с конфигурацией базы данных (БД) работают пользователи, переносит одно в другое «Конфигурация → Обновить конфигурацию базы данных» (F7). Если в базе есть сеансы, а изменения не требуют реструктуризации (модули, формы, макеты), конфигуратор предлагает кнопку «Обновить динамически»: начатые сеансы дорабатывают на старой конфигурации, новые стартуют на новой.
В клиент-серверной базе конфигурация БД лежит в служебной таблице Config, основная, ещё не перенесённая в БД, — в ConfigSave. После оборванного обновления в Config остаются строки-признаки commit, dynamicCommit, dbStruFinal (в исправной базе их нет), и платформа при каждом запуске считает операцию незавершённой — с сообщением о реструктуризации, даже если обновление было динамическим.
| Сообщение | Где | Что значит |
|---|---|---|
| «Внимание!!! При обновлении данных, после последней реструктуризации, произошла критическая ошибка. Повторить обновление?» | Конфигуратор | Прошлое обновление не завершено, можно повторить |
| «…произошла критическая ошибка. Для обновления данных запустите Конфигуратор.» | 1С:Предприятие | Пользователей не пустит до завершения операции |
| «Обнаружена незавершенная операция сохранения конфигурации» (или «…обновления конфигурации БД», «…возврата к конфигурации БД») | Конфигуратор | Оборвалась запись одной из конфигураций |
| «Конфигурация информационной базы обновлена динамически. Требуется переподключение клиента» | Старый сеанс | Нужен перезапуск |
Тексты — по платформе 8.3.27; на 8.2 первое звучало как «…произошла ошибка. Повторить обновление?».
/IBRestoreIntegrity в пакетном режиме (с 8.3.9) — ключ как раз для обновления, оборванного падением конфигуратора или выключением компьютера; подробнее — в статье о нарушенной целостности структуры конфигурации.ibcmd infobase config repair на свежих платформах: --commit доводит незавершённую операцию до конца, --rollback отменяет./RollbackCfg, затем загрузите изменения заново и обновите базу монопольно.Шаги 3 и 5 удобно выполнить скриптом. Если в имени пользователя кириллица, сохраните .bat в кодировке OEM 866.
@echo off
rem Восстановление после оборванного обновления и повторное обновление БЕЗ динамического режима.
rem Перед запуском: копия базы средствами СУБД, сеансы завершены, вход заблокирован с кодом доступа.
setlocal
set V8="C:\Program Files\1cv8\8.3.xx.xxxx\bin\1cv8.exe"
set IB=/S srv01\trade /N <администратор> /P <пароль> /UC <код доступа>
set LOG=D:\1c_logs
rem 1. Восстановить структуру после незавершенного обновления. Остальные команды ключ игнорирует,
rem поэтому это отдельный запуск. Код 0 - структура цела или восстановлена, 1 - не удалось.
%V8% DESIGNER %IB% /DisableStartupDialogs /IBRestoreIntegrity /Out %LOG%\1-restore.log
echo IBRestoreIntegrity exit code: %ERRORLEVEL%
if errorlevel 1 exit /b 1
rem 2. Повторить обновление: -Dynamic- запрещает динамический режим (по умолчанию -Dynamic+),
rem -SessionTerminate force завершает сеансы, которые мешают монопольной блокировке.
%V8% DESIGNER %IB% /DisableStartupDialogs /DisableStartupMessages /UpdateDBCfg -Dynamic- -SessionTerminate force /Out %LOG%\2-update.log
echo UpdateDBCfg exit code: %ERRORLEVEL%
rem 3. Вместо шага 2, если испорчена основная конфигурация, а конфигурация БД цела:
rem %V8% DESIGNER %IB% /DisableStartupDialogs /RollbackCfg /Out %LOG%\3-rollback.log
endlocal
Вариант с ibcmd — путь и версию подставьте свои:
rem ibcmd работает с СУБД напрямую, минуя кластер: служба 1С на это время остановлена.
rem --rollback отменяет незавершенную операцию, --commit пытается довести ее до конца.
"C:\Program Files\1cv8\8.3.xx.xxxx\bin\ibcmd.exe" infobase config repair --rollback ^
--dbms=MSSQLServer --db-server=srv01 --db-name=trade ^
--db-user=<логин SQL> --db-pwd=<пароль> ^
--user=<администратор 1С> --password=<пароль>
Если штатное не помогло, автор сайта убирал признаки прямо в СУБД: удалял из Config строки commit, dynamicCommit, dbStruFinal, очищал ConfigSave и правил статус в SchemaStorage. Служебные таблицы 1С не документированы, ошибка в условии DELETE добьёт базу, поэтому — только с копией и строго по шагам. Файловой базы приём не касается.
Пока работает кластер, у базы остаются сеансы и кэш, а править служебные таблицы нужно без подключений. Остановка отключит все базы кластера — на общем сервере нужно окно.
# Службы агента сервера 1С ищем по файлу ragent.exe: имя службы зависит от версии и установки
Get-CimInstance Win32_Service |
Where-Object { $_.PathName -like '*ragent.exe*' } |
Format-Table Name, State, PathName -AutoSize
# Остановка отключит ВСЕ базы этого кластера, а не только поврежденную
Stop-Service -Name '<Name из списка выше>'
# ... проверочные SELECT и правки в SQL ...
Start-Service -Name '<Name из списка выше>'
Разверните рядом бэкап до сбоя под именем trade_copy — с ним сравниваем. Запросы только читают.
-- Только чтение. trade - поврежденная база, trade_copy - бэкап до сбоя, развернутый рядом.
-- 1. Признаки незавершенной операции: в исправной базе этих строк обычно нет
SELECT 'trade' AS db, FileName FROM [trade].dbo.Config
WHERE FileName IN (N'commit', N'dynamicCommit', N'dbStruFinal')
UNION ALL
SELECT 'trade_copy', FileName FROM [trade_copy].dbo.Config
WHERE FileName IN (N'commit', N'dynamicCommit', N'dbStruFinal');
-- 2. Основная конфигурация, не перенесенная в конфигурацию БД (0 строк - чистить нечего)
SELECT COUNT(*) AS configsave_rows FROM [trade].dbo.ConfigSave;
-- 3. Статусы в SchemaStorage обеих баз (если таблицы нет - запрос упадет, шаг 4 пропускаем)
SELECT 'trade' AS db, Status, COUNT(*) AS rows_count
FROM [trade].dbo.SchemaStorage GROUP BY Status
UNION ALL
SELECT 'trade_copy', Status, COUNT(*)
FROM [trade_copy].dbo.SchemaStorage GROUP BY Status;
Строки-признаки, которых нет в копии, — кандидаты на удаление. ConfigSave заполнена, только пока основная конфигурация отличается от конфигурации БД: 0 строк — её не трогаем. Какая запись мешает, покажет трассировка SQL Server (Extended Events или Profiler): последний запрос к Config перед сообщением.
-- 0. Полная копия перед правками. COPY_ONLY не сбивает цепочку регламентных бэкапов
BACKUP DATABASE [trade] TO DISK = N'D:\backup\trade_before_fix.bak'
WITH COPY_ONLY, CHECKSUM, STATS = 10;
GO
-- 1. Копии служебных строк - в отдельную базу, чтобы не оставлять в базе 1С чужих таблиц
CREATE DATABASE [trade_fix];
GO
SELECT * INTO [trade_fix].dbo.Config_service FROM [trade].dbo.Config
WHERE FileName IN (N'commit', N'dynamicCommit', N'dbStruFinal');
SELECT * INTO [trade_fix].dbo.ConfigSave_copy FROM [trade].dbo.ConfigSave;
GO
-- 2. Удаляем признаки незавершенной операции. Если удалилось не 1-3 строки
-- (опечатка в условии), транзакция откатывается
USE [trade];
GO
BEGIN TRANSACTION;
DELETE FROM dbo.Config
WHERE FileName IN (N'commit', N'dynamicCommit', N'dbStruFinal');
IF @@ROWCOUNT BETWEEN 1 AND 3
COMMIT TRANSACTION;
ELSE
ROLLBACK TRANSACTION;
GO
-- 3. Только если решили отказаться от непримененной основной конфигурации:
-- DELETE FROM dbo.ConfigSave;
-- 4. Только если база все еще не открывается, а в исправной копии все строки со Status = 100:
-- SELECT * INTO [trade_fix].dbo.SchemaStorage_copy FROM dbo.SchemaStorage;
-- UPDATE dbo.SchemaStorage SET Status = 100 WHERE Status <> 100;
-- SELECT @@ROWCOUNT AS updated_rows;
-- 5. Когда база проверена и работает: DROP DATABASE [trade_fix];
Вернуть одну запись из trade_fix проще, чем разворачивать весь бэкап. Не уверены, что удалять, — убирайте по одной записи и проверяйте запуск после каждой: в сторонней статье о том же сбое на 8.2.15 хватило удалить commit и dbStruFinal.
Очистка ConfigSave по сути равносильна «Вернуться к конфигурации БД»: непримененные изменения пропадут, обновление придётся загрузить заново. SchemaStorage хранит сведения о схеме данных, значения статусов 1С не публикует. В заметке автора это последняя команда: Status = 100 всем строкам, без условия. Делайте это последним и только если в исправной копии все строки со статусом 100 — с копией таблицы и условием WHERE Status <> 100. Нет таблицы — нет и шага.
Запустите службу и первым откройте конфигуратор. Предложит повторить обновление — соглашайтесь, затем обновите конфигурацию БД монопольно и проверьте копию тестированием. Ошибки у отдельных пользователей после ремонта обычно снимает очистка кэша (см. статью о кэше сервера 1С).
Имена таблиц и колонок в нижнем регистре; удаление — блоком DO, который откатится при неожиданном числе строк:
-- PostgreSQL: таблицы и колонки 1С в нижнем регистре.
-- Копия перед правками (командная строка ОС):
-- pg_dump -U postgres -Fc -f /backup/trade_before_fix.dump trade
SELECT filename FROM config
WHERE filename IN ('commit', 'dynamicCommit', 'dbStruFinal');
-- Удаление одним блоком: при неожиданном числе строк все откатится
DO $$
DECLARE
n bigint;
BEGIN
DELETE FROM config
WHERE filename IN ('commit', 'dynamicCommit', 'dbStruFinal');
GET DIAGNOSTICS n = ROW_COUNT;
IF n NOT BETWEEN 1 AND 3 THEN
RAISE EXCEPTION 'Удалено строк: %, изменения отменены', n;
END IF;
END $$;
FileName = ' dbStruFinal' — с пробелом в начале. Хвостовые пробелы SQL Server при сравнении игнорирует, ведущие — нет: такой DELETE молча удалит 0 строк.delete from configsave без проверки уничтожает непримененную основную конфигурацию — сначала SELECT COUNT(*) и копия.UPDATE SchemaStorage SET Status = 100 без WHERE меняет все строки — добавлены условие и сверка с копией./UpdateDBCfg по умолчанию -Dynamic+, и ночной скрипт сам обновит базу динамически, если кто-то задержался. Нужны -Dynamic- и -SessionTerminate force, как в скрипте выше; у ibcmd — --dynamic=disable в infobase config apply.Разработку ведите в копии или через хранилище конфигурации, а в рабочую базу переносите одним обновлением в окно.
Функция КонфигурацияБазыДанныхИзмененаДинамически() возвращает Истина в сеансах, начатых до обновления; доступна на сервере, в толстом клиенте и внешнем соединении. В типовых на БСП проверка встроена: в УТ 11.4 раз в 20 минут приходит оповещение «Установлено обновление программы» со ссылкой на форму КонтрольДинамическогоОбновления. Код ниже — для самописных конфигураций и случаев, когда оповещения мало.
Листинг 1 — модуль формы, открытой весь сеанс (начальная страница, рабочее место кассира). В модуле управляемого приложения обработчик ожидания должен быть экспортной процедурой, а серверную функцию придётся вынести в общий модуль с флагом «Вызов сервера».
// Модуль формы, которая открыта весь сеанс: начальная страница, рабочее место кассира и т. п.
// Раз в 10 минут спрашивает сервер, не обновили ли конфигурацию динамически,
// и предлагает перезапуск. Если пользователь откладывает - напоминает через 15 минут.
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Если обработчик ПриОткрытии у формы уже есть, добавьте в него только эту строку
ПодключитьОбработчикОжидания("ПроверитьОбновлениеКонфигурации", 600);
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьОбновлениеКонфигурации()
ТекстПредупреждения = ТекстПредупрежденияОбОбновлении();
Если ПустаяСтрока(ТекстПредупреждения) Тогда
Возврат; // конфигурацию не меняли - ждем следующего срабатывания
КонецЕсли;
// Признак не сбросится до перезапуска, поэтому сервер больше не опрашиваем
ОтключитьОбработчикОжидания("ПроверитьОбновлениеКонфигурации");
ПоказатьОповещениеПользователя(НСтр("ru = 'Программа обновлена'"), , ТекстПредупреждения, ,
СтатусОповещенияПользователя.Важное, "ДинамическоеОбновлениеКонфигурации");
ПредложитьПерезапуск();
КонецПроцедуры
&НаКлиенте
Процедура ПредложитьПерезапуск()
Кнопки = Новый СписокЗначений;
Кнопки.Добавить(КодВозвратаДиалога.Да, НСтр("ru = 'Перезапустить сейчас'"));
Кнопки.Добавить(КодВозвратаДиалога.Нет, НСтр("ru = 'Напомнить через 15 минут'"));
Оповещение = Новый ОписаниеОповещения("ПослеОтветаОПерезапуске", ЭтотОбъект);
ТекстВопроса = НСтр("ru = 'Администратор обновил программу. Сохраните открытые документы
|и перезапустите ее, чтобы работать в новой версии.'");
ПоказатьВопрос(Оповещение, ТекстВопроса, Кнопки, 300, КодВозвратаДиалога.Нет,
НСтр("ru = 'Требуется перезапуск'"));
КонецПроцедуры
&НаКлиенте
Процедура ПослеОтветаОПерезапуске(Ответ, ДополнительныеПараметры) Экспорт
Если Ответ = КодВозвратаДиалога.Да Тогда
// Истина в первом параметре: формы с несохраненными данными спросят, что с ними делать
ЗавершитьРаботуСистемы(Истина, Истина);
КонецЕсли;
// «Напомнить», таймаут или пользователь отменил закрытие ради несохраненных данных:
// однократный обработчик спросит снова через 15 минут (при выходе он уже не сработает)
ПодключитьОбработчикОжидания("ПредложитьПерезапуск", 900, Истина);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ТекстПредупрежденияОбОбновлении()
Если Не КонфигурацияБазыДанныхИзмененаДинамически() Тогда
Возврат "";
КонецЕсли;
// Сеанс по-прежнему работает на той конфигурации, с которой стартовал
Текст = НСтр("ru = 'Конфигурацию базы обновили во время вашей работы.'");
Если Не ПустаяСтрока(Метаданные.Версия) Тогда
Текст = Текст + " " + СтрШаблон(НСтр("ru = 'У вас открыта версия %1.'"), Метаданные.Версия);
КонецЕсли;
ЗаписьЖурналаРегистрации("КонтрольОбновления.ТребуетсяПерезапуск",
УровеньЖурналаРегистрации.Информация, , , Текст);
Возврат Текст;
КонецФункции
Листинг 2 — защита операций, которые нельзя выполнять старым кодом: обмена, закрытия месяца, проведения после исправления алгоритма.
// Общий модуль ОбновлениеКонфигурацииСервер, флаги «Сервер» и «Вызов сервера».
// Не дает выполнить важную операцию старым кодом, пока сеанс не перезапущен
// после динамического обновления. Вызов в начале операции, например в ОбработкаПроведения:
// ОбновлениеКонфигурацииСервер.ПроверитьАктуальностьСеанса("Проведение реализации");
Процедура ПроверитьАктуальностьСеанса(Знач ИмяОперации) Экспорт
Если Не КонфигурацияБазыДанныхИзмененаДинамически() Тогда
Возврат;
КонецЕсли;
ТекстОшибки = СтрШаблон(
НСтр("ru = 'Операция «%1» остановлена: программу обновили во время вашей работы.
|Сохраните данные, перезапустите программу и повторите действие.'"), ИмяОперации);
// Запись независима от транзакции: останется в журнале и после отката проведения
ЗаписьЖурналаРегистрации("КонтрольОбновления.ОперацияОстановлена",
УровеньЖурналаРегистрации.Предупреждение, , , ТекстОшибки);
ВызватьИсключение ТекстОшибки;
КонецПроцедуры
Листинг 3 — для администратора: кто ещё не перезапустился. Время обновления берём из журнала регистрации (событие «Информационная база. Изменение конфигурации базы данных»), сеансы — из списка активных.
// Тот же общий модуль. Кто до сих пор работает на прежней конфигурации:
// сеансы, начатые раньше последнего изменения конфигурации БД.
Функция СеансыНаПрежнейКонфигурации() Экспорт
Если Не ПравоДоступа("ЖурналРегистрации", Метаданные)
Или Не ПравоДоступа("АктивныеПользователи", Метаданные) Тогда
ВызватьИсключение НСтр("ru = 'Нужны права «Журнал регистрации» и «Активные пользователи».'");
КонецЕсли;
Результат = Новый ТаблицаЗначений;
Результат.Колонки.Добавить("НомерСеанса", Новый ОписаниеТипов("Число"));
Результат.Колонки.Добавить("Пользователь", Новый ОписаниеТипов("Строка"));
Результат.Колонки.Добавить("Приложение", Новый ОписаниеТипов("Строка"));
Результат.Колонки.Добавить("Компьютер", Новый ОписаниеТипов("Строка"));
Результат.Колонки.Добавить("НачалоСеанса", Новый ОписаниеТипов("Дата"));
// Последнее событие «Изменение конфигурации базы данных» за неделю.
// Журнал хранит время сервера, поэтому отбор считаем от ТекущаяДата()
Отбор = Новый Структура;
Отбор.Вставить("Событие", "_$InfoBase$_.DBConfigUpdate");
Отбор.Вставить("ДатаНачала", ТекущаяДата() - 7 * 86400);
События = Новый ТаблицаЗначений;
ВыгрузитьЖурналРегистрации(События, Отбор, "Дата", , 1);
Если События.Количество() = 0 Тогда
Возврат Результат; // за неделю конфигурацию БД не обновляли
КонецЕсли;
МоментОбновления = События[0].Дата;
Для Каждого Сеанс Из ПолучитьСеансыИнформационнойБазы() Цикл
// Начатые после обновления уже на новой конфигурации; консоль кластера не в счет
Если Сеанс.НачалоСеанса >= МоментОбновления Или Сеанс.ИмяПриложения = "SrvrConsole" Тогда
Продолжить;
КонецЕсли;
СтрокаСеанса = Результат.Добавить();
СтрокаСеанса.НомерСеанса = Сеанс.НомерСеанса;
СтрокаСеанса.Пользователь = ?(Сеанс.Пользователь = Неопределено, "", Сеанс.Пользователь.Имя);
СтрокаСеанса.Приложение = ПредставлениеПриложения(Сеанс.ИмяПриложения);
СтрокаСеанса.Компьютер = Сеанс.ИмяКомпьютера;
СтрокаСеанса.НачалоСеанса = Сеанс.НачалоСеанса;
КонецЦикла;
Результат.Сортировать("НачалоСеанса");
Возврат Результат;
КонецФункции
&НаСервереБезКонтекста: в тонком клиенте функции нет, а без контекста на сервер не уходят данные формы — вызов раз в 10 минут у каждого пользователя остаётся лёгким.ОтключитьОбработчикОжидания: признак до перезапуска не сбросится, дальше напоминаем однократным обработчиком без обращений к серверу.ПоказатьВопрос(…, 300, …): через 5 минут окно закроется само с результатом КодВозвратаДиалога.Таймаут, и напоминание просто переподключится.ЗавершитьРаботуСистемы(Истина, Истина): формы спросят про несохранённые данные, затем стартует новый сеанс. Если пользователь отменит закрытие, сработает напоминание; из модальной формы метод не работает.ВызватьИсключение откатывает проведение, а запись журнала остаётся: по умолчанию она независима от транзакции.ВыгрузитьЖурналРегистрации(…, 1) читает одно последнее событие по отбору; без права «Журнал регистрации» — исключение.IF @@ROWCOUNT BETWEEN 1 AND 3 и RAISE EXCEPTION страхуют от DELETE с неверным условием./IBRestoreIntegrity.' dbStruFinal' — удалено 0 строк, а вы уверены, что запись убрана./UpdateDBCfg. Кто-то не вышел, изменения без реструктуризации — база обновилась динамически, и утром часть пользователей на старой версии.Таблицы данных динамическое обновление не перестраивает, страдает конфигурация. Удаление признаков в Config данные не трогает, а очистка ConfigSave уничтожает непримененные изменения конфигурации. Поэтому копия — перед любым шагом.
Изменения требуют реструктуризации; файловая база работает с клиентами через веб-сервер; базу недавно перевели на 8.3.20 или новее со старой версии — по списку изменений 8.3.20 динамическое не выполняется до первого обычного обновления (или реструктуризации в тестировании и исправлении).
Да: до перезапуска сеанс работает на прежней конфигурации. В типовых на БСП напомнит оповещение, в самописных — листинг 1.
Нет, таблицы лежат внутри 1Cv8.1CD. Для файловой базы — /IBRestoreIntegrity, тестирование и исправление, chdbfl и восстановление из копии.
Если база стоит, копии до сбоя нет, а править служебные таблицы на рабочем сервере страшно, поручите восстановление программисту, который делал это не раз. Разместите задачу — оценка бесплатная, ответ в течение рабочего дня; там же можно заказать безопасный регламент обновлений. Другие направления — на странице услуг.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.