gifts2017

Апрельские "нешутки", записанные в пятницу не 13-го (продолжение)

Опубликовал Даниил Матвеев (cargobird) в раздел Программирование - Практика программирования

Еще одна байка про незадачливого кодера:
Загадочный РЛС или почему не могут работать "ПолныеПрава"

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

Но... По субъективным ощущениям (это которые без замеров) все стало чуть-чуть медленнее работать.

Кодер знал, что за кажущейся простотой ограничения доступа вроде

ОрганизацияВШапке("Организация")

скрывается подобный монстр-запрос:

##Если &ИспользоватьОграниченияПравДоступаНаУровнеЗаписей ##Тогда
ТекущаяТаблица ИЗ #ТекущаяТаблица КАК ТекущаяТаблица
    ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ
        СоставГруппы.Ссылка КАК ГруппаПользователей
    ИЗ
        Справочник.ГруппыПользователей.ПользователиГруппы КАК СоставГруппы
    ГДЕ
        СоставГруппы.Пользователь = &ТекущийПользователь) КАК ГруппыПользователей
    ПО (&ИспользоватьОграниченияПравДоступаНаУровнеЗаписей)
ГДЕ (&ИспользоватьОграниченияПравДоступаНаУровнеЗаписей = ЛОЖЬ
            ИЛИ (НЕ 1 В
                    (ВЫБРАТЬ ПЕРВЫЕ 1
                        1 КАК ПолеОтбора
                    ИЗ
                        РегистрСведений.НазначениеВидовОбъектовДоступа КАК НазначениеВидовОбъектовДоступа
                    ГДЕ
                        НазначениеВидовОбъектовДоступа.ГруппаПользователей = ГруппыПользователей.ГруппаПользователей
                        И ВЫБОР
                            КОГДА НазначениеВидовОбъектовДоступа.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации)
                            	И ТекущаяТаблица.#Параметр(1) ССЫЛКА Справочник.Организации
                            	И НЕ ТекущаяТаблица.#Параметр(1) = ЗНАЧЕНИЕ(Справочник.Организации.ПустаяСсылка)
                                ТОГДА ВЫБОР
                                        КОГДА 1 В
                                                (ВЫБРАТЬ ПЕРВЫЕ 1
                                                    1
                                                ИЗ
                                                    (ВЫБРАТЬ
                                                        1 КАК ПолеОтбора
                                                    ) КАК Оптмизация ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НастройкиПравДоступаПользователей КАК НастройкиПравДоступаПользователей
                                                        ПО
                                                            НастройкиПравДоступаПользователей.ОбъектДоступа = ТекущаяТаблица.#Параметр(1)
                                                                И НастройкиПравДоступаПользователей.ВидОбъектаДоступа = ЗНАЧЕНИЕ(Перечисление.ВидыОбъектовДоступа.Организации)
                                                                И (НастройкиПравДоступаПользователей.Пользователь = НазначениеВидовОбъектовДоступа.ГруппаПользователей
                                                                    ИЛИ НастройкиПравДоступаПользователей.Пользователь = ЗНАЧЕНИЕ(Справочник.ГруппыПользователей.ВсеПользователи)))
                                            ТОГДА ИСТИНА
                                        ИНАЧЕ ЛОЖЬ
                                        КОНЕЦ
                                        ИНАЧЕ ИСТИНА
                                    КОНЕЦ = ЛОЖЬ))
                        И НЕ ГруппыПользователей.ГруппаПользователей ЕСТЬ NULL)
##КонецЕсли

От такого все что угодно можно ожидать, подумал кодер и решил придумать способ оптимизации.

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

Если НЕ РольДоступна("ПолныеПрава") Тогда
	Если Константы.ИспользоватьОграниченияПравДоступаНаУровнеЗаписей.Получить() Тогда
		Запрос = Новый Запрос("ВЫБРАТЬ
		                      |	ГруппыПользователейПользователиГруппы.Ссылка
		                      |ПОМЕСТИТЬ ВТ_ГП
		                      |ИЗ
		                      |	Справочник.ГруппыПользователей.ПользователиГруппы КАК ГруппыПользователейПользователиГруппы
		                      |ГДЕ
		                      |	ГруппыПользователейПользователиГруппы.Пользователь = &Пользователь
		                      |;
		                      |
		                      |////////////////////////////////////////////////////////////////////////////////
		                      |ВЫБРАТЬ РАЗРЕШЕННЫЕ
		                      |	&Пользователь КАК Пользователь,
		                      |	НастройкиПравДоступаПользователей.ОбъектДоступа КАК Организация,
		                      |	НастройкиПравДоступаПользователей.Запись,
		                      |	НастройкиПравДоступаПользователей.Отчеты
		                      |ПОМЕСТИТЬ ВТ_Доступ
		                      |ИЗ
		                      |	РегистрСведений.НастройкиПравДоступаПользователей КАК НастройкиПравДоступаПользователей
		                      |ГДЕ
		                      |	НастройкиПравДоступаПользователей.Пользователь В
		                      |			(ВЫБРАТЬ
		                      |				ВТ_ГП.Ссылка
		                      |			ИЗ
		                      |				ВТ_ГП КАК ВТ_ГП)
		                      |		КОНЕЦ
		                      |
		                      |СГРУППИРОВАТЬ ПО
		                      |	НастройкиПравДоступаПользователей.ОбъектДоступа,
		                      |	НастройкиПравДоступаПользователей.Запись
		                      |;
		                      |
		                      |////////////////////////////////////////////////////////////////////////////////
		                      |ВЫБРАТЬ
		                      |	ВТ_Доступ.Пользователь,
		                      |	ВТ_Доступ.Организация
		                      |ИЗ
		                      |	ВТ_Доступ КАК ВТ_Доступ
		                      |;
		                      |
		                      |////////////////////////////////////////////////////////////////////////////////
		                      |ВЫБРАТЬ
		                      |	ВТ_Доступ.Пользователь,
		                      |	ВТ_Доступ.Организация
		                      |ИЗ
		                      |	ВТ_Доступ КАК ВТ_Доступ
		                      |ГДЕ
		                      |	ВТ_Доступ.Запись");
		ТекущийПользователь = ПараметрыСеанса.ТекущийПользователь;
		Запрос.УстановитьПараметр("Пользователь", ТекущийПользователь);
		МассивРезультатов = Запрос.ВыполнитьПакет();
		РезультатЗапросаЧтение = МассивРезультатов[2].Выгрузить();
		РезультатЗапросаЗапись = МассивРезультатов[3].Выгрузить();
		
		МассивОрганизацийДоступныхДляЧтения = РезультатЗапросаЧтение.ВыгрузитьКолонку("Организация");
		ПараметрыСеанса.СписокОрганизацийДоступныхДляЧтения = Новый ФиксированныйМассив(МассивОрганизацийДоступныхДляЧтения);
		МассивОрганизацийДоступныхДляЗаписи = РезультатЗапросаЗапись.ВыгрузитьКолонку("Организация");
		ПараметрыСеанса.СписокОрганизацийДоступныхДляЗаписи = Новый ФиксированныйМассив(МассивОрганизацийДоступныхДляЗаписи);		
	КонецЕсли;	
КонецЕсли; 

И монстр-запрос для чтения превратился во вполне компактный:

##Если &ИспользоватьОграниченияПравДоступаНаУровнеЗаписей ##Тогда
ТекущаяТаблица ИЗ #ТекущаяТаблица КАК ТекущаяТаблица
    ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
	1 КАК Поле1) КАК РазрешениеДоступа
    ПО (&ИспользоватьОграниченияПравДоступаНаУровнеЗаписей)
ГДЕ 
(ТекущаяТаблица.#Параметр(1) В (&СписокОрганизацийДоступныхДляЧтения) 
ИЛИ ТекущаяТаблица.#Параметр(1) = ЗНАЧЕНИЕ(Справочник.Организации.)) ##КонецЕсли

Для записи аналогично, только подставить другой параметр сеанса.

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

Наступило утро. Обычные пользователи без полных прав вошли в программу и стали работать как обычно.

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

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

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

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

А зря. У пользователей с полными правами, (наверное для большей полноты прав) были добавлены еще по нескольку ролей, эти роли и пытались получить список доступных организаций через неинициализированный параметр сеанса.

Попытка снять "лишние" роли привела к отключению у полноправных пользователей некоторых возможностей. Это, в свою очередь, произошло от того, что часть из этих "лишних" ролей использовалась только в коде для включения определенного функционала (речь идет об УТ 10.3, функциональные опции еще только снятся), но поскольку роли были скопированы с типовых ролей, то и потянули за собой и шаблоны ограничений доступа, и ограничения доступа к метаданным.

Финал оптимистичный, в стиле анекдота про отца-программиста и сына: "Знаешь что, сынок! Не трогай-ка ты работающую систему!" - наборы ролей пользователей были восстановлены в прежнем состоянии, над условием "Если не РольДоступна("ПолныеПрава")..." появилась пара строк инициализации параметров сеанса пустыми фиксированными массивами и все продолжили спокойно работать до следущего внедрения...

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Андрей Карпов (karpik666) 10.04.15 09:12
Хм, А просто условие убрать на полные права и в "иначе" записать
ПараметрыСеанса.СписокОрганизацийДоступныхДляЧтения = Новый ФиксированныйМассив();
        ПараметрыСеанса.СписокОрганизацийДоступныхДляЗаписи = Новый ФиксированныйМассив();    

На самом деле не очень страшная история, глядя на название хотелось таких ужасов, чтобы мороз по коже=)
По мне мораль, что все нужно тестировать, прежде чем накатывать.
2. Даниил Матвеев (cargobird) 10.04.15 09:44
(1) karpik666

не очень страшная история


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

все нужно тестировать


Так все и было протестировано, только под неполными правами) Что так будут вести себя "полные права" ожидалось меньше всего)

просто условие убрать на полные права


В итоге да, было убрано условие на полные права ..
3. Сергей (necropunk) 10.04.15 10:58
Да-да-да, знакомый до боли затык. Влипал я так с полными правами тоже...
cargobird; +1 Ответить
4. Василий Коровин (vasyak319) 10.04.15 11:03
История на тему "какие траблы могут случиться, если дорабатывать систему, уже доработанную рукожопами". Да, в общем, любые.
А можно вообще про каждую обнаруженную ошибку статью писать (в принципе, эта статья как раз такая и есть). А можно анекдоты постить. А почему нет, если начисляют по 10 СМ за каждую?
alest; wolfsoft; +2 Ответить 1
5. Даниил Матвеев (cargobird) 10.04.15 11:55
(4) vasyak319, в этой статье кроме обнаруженной ошибки (достаточно серьезной, и, наверное, стоящей того, чтобы поделиться), приведен скромный опыт сокращения запроса РЛС путем передачи фиксированного массива в параметр сеанса и кроме того, нашелся сотоварищ по влипанию в подобную ситуацию. А 10 стартмани это скорее приятный бонус, а не цель...
6. Василий Коровин (vasyak319) 10.04.15 12:15
(5) cargobird, да все влипали. В кучу разных ситуаций, ибо не оскудела земля русская рукожопами. Мне как-то вообще попалась конфа (КАТАП), доработанная платиново-иридиевым эталоном рукожопа.
Чувак писал что-то вроде:

Процедура ПриОткрытии()
	ВыбКлиент.Очистить();
	ВЫбКлиент=ВосстановитьЗначение("Выбклиент_ОтчетПоКлиентам");
	ВыбКлиент.Очистить();
	Номенклатура=Справочники.ОсобКлиенты.Выбрать(,,,"Наименование УБЫВ");
	Пока Номенклатура.Следующий() Цикл
		Объект=Номенклатура.Ссылка;
		Клиент=Объект.ПолучитьОбъект();
		Попытка
			ВыбКлиент.Добавить(Клиент.Поставщик);
		Исключение
		КонецПопытки
	КонецЦикла;
...
...Показать Скрыть


ВыбКлиент-список значений на форме отчёта. Уродливые кнопки для его редактирования - всё, как положено. При этом первое, что делала процедура Сформировать - очищала ВыбКлиент и заполняла заново примерно таким же алгоритмом.
В некоторых документах был булевый реквизит, который на форме документа выглядел флажком с заголовком "Орехова Т.". В процедурах ПередЗаписью проверки типа
Если ТекущийПользователь.Наименование="Иванова Света"
Итэде итэпэ. И что?

А вы приводите пример заурядной проблемы, причины которой очевидны, способ выяснения проблемных мест очевиден и способ исправления тоже очевиден.
7. Андрей Карпов (karpik666) 10.04.15 12:24
(6) vasyak319,
Если ТекущийПользователь.Наименование="Иванова Света"
Видимо в этой организации Иванова Светлана будет работать до победного, а если и уволиться, то в вакансии на ее место будет обязательное требование: Требуется человек с именем "Иванова Света" или изменить на соответствующее в ЗАГС=)
alest; odin777; +2 Ответить 1
8. Даниил Матвеев (cargobird) 10.04.15 12:24
(6) vasyak319, боюсь показаться нудным, но способ сокращения запроса РЛС вроде ничего себе так, хотя и не протестирован на производительность)

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

Скоро напишу "серьезную" статью по РЛС, а тут - извиняйте, ежели чего...
9. Андрей Карпов (karpik666) 10.04.15 12:26
(8) cargobird, ну так форум что ли отменили?
10. Даниил Матвеев (cargobird) 10.04.15 12:33
(9) karpik666, точно, как-то об этом не подумал)
11. Михаил Рожков (Templ) 10.04.15 21:04
(6) vasyak319, (7) karpik666,
Если ТекущийПользователь.Наименование="Иванова Света"
Видимо в этой организации Иванова Светлана будет работать до победного, а если и уволиться, то в вакансии на ее место будет обязательное требование: Требуется человек с именем "Иванова Света" или изменить на соответствующее в ЗАГС=)

А как надо писать?
12. Даниил Матвеев (cargobird) 10.04.15 22:08
(11) Templ, как минимум, если конфигурация нетиповая,
Если РольДоступна("ИвановаСвета") Тогда ... КонецЕсли;

Тогда пользователю с любым именем можно назначить функционал Светы Ивановой)
13. Василий Коровин (vasyak319) 11.04.15 01:18
(8) cargobird,
способ сокращения запроса РЛС вроде ничего себе так
таки да.

(11) Templ, зависит от ситуации. В том конкретном случае годилось:
Если ЭтоРуководительОтделаПродаж() Тогда...

или:
Если ОбщегоНазначения.ЕстьПраво(ПланыВидовХарактеристик.ДополнительныеПраваПользователей.ПравоТворитьСЗаказомПокупателяЛюбоеНепотребство) Тогда...

или даже:
Если ТекущийПользователь=Константы.ТотЕдинственныйПользовательКоторомуМыРазрешаемУродоватьЗаказКакБогЧерепаху.Получить() Тогда...


Запомни, Templ, никаких констант в коде! Никогда! Или я приду за тобой... Не, ну почему в этом форуме нет зловещих смайликов? Вообще никаких нету :(
Chif13; odin777; hulio; baton_pk; karpik666; cargobird; +6 Ответить 1
14. Михаил Рожков (Templ) 11.04.15 22:37
У нас есть вещи и пострашнее констант. Например Запросы в цикле. Соответсвенно тормоза не хилые. (но это не я)
15. Александр Аляев (alyaev.a.v) 12.04.15 11:09
(13) vasyak319, А чем так страшны константы в коде,особенно вне транзакций?
16. Василий Коровин (vasyak319) 12.04.15 14:38
(15) alyaev.a.v, по-моему вы путаете прикладные объекты Константы с термином "константы в коде".
17. Александр Аляев (alyaev.a.v) 12.04.15 23:46
(16) vasyak319, Возможно, я просто подумал коммент про константы относился к "ТекущийПользователь=Константы.ТотЕдинственныйПользовательКоторомуМыРазрешаемУродоватьЗаказ­КакБогЧерепаху.Получить()"

а похоже он относился "Если ТекущийПользователь.Наименование="Иванова Света"",

попутал :)
18. Алексей Молодов (Allexe8.1) 15.04.15 20:21
(5) cargobird,
опыт сокращения запроса РЛС путем передачи фиксированного массива в параметр сеанса
Опасная штука. Если массив будет большим, условие (IN) будет выполняться через построчное создание ВТ (сколько элементов массива - столько insert-ов) и левое соединение с ней.
19. Даниил Матвеев (cargobird) 15.04.15 21:17
(18) Allexe8.1, спасибо за предупреждение, протестирую.

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

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