Файловое хранилище без ActiveDirectory, но с правами, своими руками.

13 11

Сначала будет много текста. Код и картинки в конце.

Проблема

Со временем, при работе с системой Directum, база данных растет. Растет, прежде всего, за счет самих документов. При очень больших объемах базы данных становится неудобно её обслуживать. Как решение, предлагается использовать файловое хранилище. Но, не всё так радужно, как хотелось бы. Для «правильного» назначения прав доступа на файлы в хранилище, должен быть настроен домен в сети организации. Альтернатива домену – это механизм рабочих групп Windows, или статические права. При использовании рабочих групп, каждого пользователя нужно вручную заводить на локальном компьютере и сервере, а это огромная работа по администрированию. Если же использовать статические права доступа, то либо, все пользователи (и не только пользователи Directum) имеют права на просмотр всех документов, и не имеют прав на изменение, либо все имеют права на изменение всех документов, либо не имеют прав на сетевую папку вовсе.

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

Поиск решения

Раз за разом мы обдумывали, как бы сделать так, что бы и волки сыты и овцы целы и файлы хранить на диске, и права на них раздавать в соответствии с правами Directum. Предлагались разные варианты. Например, при переводе в файловое хранилище, не давать доступ на папку хранилища всем пользователям, а с файлами работать через Web доступ. Но, все эти варианты в том или ином виде неудобны или ограничивают базовый функционал.

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

Проработка

Событий открытия текста документа у нас в системе нет. Есть только события открытия набора данных и показа карточки. Показ карточки не подходит, так как не срабатывает при просмотре текста документа. Событие «Запись Открытие» не подходит. В этот момент документ уже загружен и заблокирован от изменений. А вот событие «Набор данных Открытие» подошло. В момент его выполнения, документ ещё не открыт, и если заставить клиентскую машину ждать, то в это время можно переместить документ из одного хранилища в другое, а после продолжить открытие документа. Схема оказалась принципиально рабочей.

Осталось дело за деталями. Каким-то образом пнуть, то есть запустить, сценарий на удаленной машине, с которой возможно перевести документ из файлового хранилища. Например, на самом файловом хранилище. Делать это надо в режиме on-line. Требуется дождаться окончания операции, поэтому различные варианты очереди тут не подойдут. А подойдет, например, Web сервис.

Реализация

На машине с файловым хранилищем развернули IIS Web сервер. Настроили его на работу с PHP интерпретатором. В IIS создали сайт, написали PHP скрипт для подключения к системе через Com объектную модель, выдали права пользователю, от которого выполняются скрипты сайта на Com объекты Directum’а и на папки, где он установлен.

Написали сценарий для перевода документов между хранилищами. Этот сценарий используется в PHP скрипте. Написали функцию, которая отправляет запрос на перенос  на Web сервер. Написали функцию, которая переводит документы в SQL хранилище.  Последнюю разместили в событии «Набор данных Открытие» типов карточек документов.

Результат

Вся эта разработка и тестирование проводилось на тестовой системе и на рабочей системе пока не применялось. Переводить документы в файловое хранилище можно каким-либо сценарием. К файловому хранилищу напрямую пользователи доступ не имеют. При обращении к документу, который находится в файловом хранилище, он перемещается в SQL хранилище «налету» и пользователь работает с этим документом уже из SQL хранилища. Задержка открытия при переводе в SQL примерно от 2 до 15 секунд в зависимости от размера документов и количества версий. При работе из Web доступа пока проверялась только ситуация, когда Web и служба ФХ на одной машине. В этом случае перевод документа в SQL не осуществляется, а Web доступ работает напрямую с ФХ. В дальнейшем планируем развернуть эту разработку на рабочей системе и использовать для хранения старых файлов.


Схема работы решения.

Код

1. Сценарий перевода в SQL хранилище (__MoveToSQLStorage)

docId = ValueByName(object.params; "docId"; nil)
if Assigned(docId)
  ed = EDocuments.getObjectById(docId)
  ed.MoveToStorage(EDocuments.GetStorageByName("SSS"))
endif

2. PHP Скрипт (toSQLStorage.php)

GetApplication("ServerName=$server;DBName=$dbName;UserName=;Password=", false);
		$srcipt = $app->ScriptFactory->GetObjectByName("__MoveToSQLStorage");
		$srcipt->params->setVar("docId", $docId);
		$srcipt->execute();
		echo "1";
	}catch (Exception $e){
		echo ''.  $e->getMessage() ."";
	}
?>

3. Функция вызывающая php скрипт (__httpMoveToSQLStorage(docId; WebServer))

connection = Application.Connection
server = connection.ServerName
DBName = connection.DatabaseName
script = "toSQLStorage.php"
Params = "docId="& docId &"&S="& server &"&D="& DBName
URL = WebServer & script & "?" & Params
try
  XHR = CreateObject("Msxml2.XMLHTTP.3.0")
  XHR.Open("POST"; URL; false)
  XHR.Send()
  responce = XHR.ResponseText
finally
  XHR = nil
endfinally

4. Основная функция (__checkFileStorage(this))

storages = arrayof("SF1") // Файловые хранилища, откуда нужно переносить в SQL

serverExceptions = arrayOf("") // Имена серверов, от которых не нужно посылать HTTP запрос на смену хранилища. В том числе, обязательно,  сервер обработки HTTP запросов

id = this.id
sql = "
  select s.Kod
  from SBEDoc d
  inner join MBAnalit s on s.Analit = d.Storage
  where d.XRecID = '"& id &"'
"

strg = trim(sql(sql))

if in(storages; strg)
  if not in(serverExceptions; UpperCase(Application.connection.hostName))
    __httpMoveToSQLStorage(id; Конст("__chengeStorageURLServer"))
  endif               
endif

Последнюю функцию помещаем в событие "Набор данных: Открытие", в параметр передаем Object и получаем Profit...

Интересное решение! Если есть более подробные данные о быстродействии решения, то прошу уточнить на каких аппаратных ресурсах располагается тестовое ФХ, его размер, число исключений переноса ЭД в логах службы ФХ, число документов в ФХ вообще.

PS: как часто планируете возвращать все "на круги своя"? То есть с какой периодичностью документы из БД вновь перемещаются в файловые хранилища? 

Михаил Тарасов

Константин, на данный момент серьезного нагрузочного тестирования не проводилось. Всё решение (в том числе и БД тестовой системы) расположена на персоналке (2 ядра 3.10 Ghz 4 гига оперативки). В файловом хранилище пока находится десяток, два документов.

Точных временных замеров не проводилось. Маленький документ (около 1 МБ) загружался из ФХ в SQL примерно 2-3 секунды. Документ с несколькими версиями, одна из которых - 90 МБ примерно 12-15 секунд. 

По поводу возврата: На тестовой базе загружали в ФХ по сути в ручную (Аналогичный сценарий через PHP, только загружает в указанную ФХ. Запускали из адресной строки.). На рабочей планируется загружать сценарием, либо по дате создания, либо, по последнему использованию (строки из истории). Допустим, документы, созданные год назад. Или документы, последний раз открывали которые, пол года назад... Пока с этим моментом не определились. Сам сценарий перевода запускать будем раз в сутки ночью. (объем переносящихся в ФХ документов будет примерно равен тому, сколько в сутки вносится...)

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

Михаил Тарасов

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

Спасибо за разъяснения! Удачи с продвижением в продуктивной среде!

Сергей Камышев

Главная проблема все же здесь:

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

А может и нет. Т.е. вам просто надо взять и документы положить в другое место, чтобы бекап БД и не занимал уйму времени и места, так сделайте это на уровне файловых групп, пусть все документы лежат на медленном, большом диске и для них будет отдельный бекап.
 К стати такой том можно еще архивировать средствами SQL, учтите что при этом вырастет нагрузка на ЦП.

Либо второе, сделайте секционирование если у вас MSSQL Enterprise, тут даже будет лучше в отдельную секцию можно складывать не все, а только "старые документы". Я проверял, и это работает. Немного усложниться администрирование, но при этом оперативный бекап будет маленький.

В общем я к тому что проблему большого объема данных вот таким вот способом ИМХО решать очень не хорошо, прежде всего тормоза, ну и второе это все равно данные постоянно загоняем в SQL. Были ли рассмотрены другие варианты решения проблемы, и какую проблему все же решали?

Сергей Камышев
Может быть потому что использование FILESTREAM сделает не нужным их сервис?

Я думаю, что не в этом дело, а в том, что сервис был написан за несколько лет до SQL2008, и так как он все еще не плохо работает, то зачем что-то менять?
Во-вторых, вы сами пользовались тем, о чем пишите? там куча ограничений, файлы должны располагается только на том же хосте (возможно с помощью каких-то манипуляций и можно сделать по-другому, но не штатными средствами SQL), на папку с фалами должны быть права самого SQL + что-то еще не изучал подробнее, как там будет работать полнотекстовый поиск с объемом в сотни Гб никто не тестировал. Конечно то что служба не работает вне домена, это плохо, но если бы такие проблемы были у все подряд, я думаю эту проблему бы решили раньше.

Александр Стуликов
возможно с помощью каких то манипуляций и можно сделать по другому, но не штатными средставми SQL

А зачем средствами SQL? И зачем вообще держать файлы на хосте? Для этого есть SUN. 

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

У нас в FILESTREAM хранятся только сканы, поэтому не знаю как будет работать полнотекстовый поиск, но думаю, это можно выяснить, 

PS самостоятельно включить FILESTREAM  в Директуме не удастся, т.к. тип поля, где хранятся документы не соответствует.

Сергей Камышев
А зачем средствами SQL?

Вот вы сами и ответили на свой вопрос, действительно зачем.

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

Я имел ввиду ограничения, про которые Вы написали.

Я же не навязываю свое мнение, просто если такая возможность будет - это будет очень хорошо!

Анатолий Придыбайло

Подскажите а реально сейчас с появление службы серверных событий реализовать данный механизм с его помощью, без IIS и PHP скрипта?

Анатолий Придыбайло: обновлено 21.02.2019 в 11:08
Михаил Тарасов

Реально, но работает медленнее. 

Общая схема такая: Отправляем событие на сервер и входим в цикл. В цикле раз в N секунд (N может быть 1/2 или 1/5 или ..) отслеживаем состояние события и при его завершении выходим из цикла, позволяем открыть документ.

Опыт с другой разработкой показывает, что такая схема работает медленнее, нежели Web сервер. В случае с Web  сервером, процесс обработки запускается сразу же. В случае со службой, не факт. Она может быть занята другими "делами". И сообщение о том, что перенос закончен поступает сразу же. А тут в среднем будет задержка в N/2. 

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

Михаил Тарасов: обновлено 21.02.2019 в 11:14

Авторизуйтесь, чтобы написать комментарий