Хранение файлов пользователей на файловой системе
От: halo Украина  
Дата: 25.12.19 17:26
Оценка:
Работаю над старым проектом, в котором пользовательские файлы хранятся прямо на файловой системе. Схема размещения файлов подчиняется следующему шаблону:

/files/$TYPE/$TENANT_ID/$FILENAME

где:

* $TYPE -- предназначение файлов, жёстко определяемое системой;
* $TENANT_ID -- код пользователя, обычная целочисленная строка без паддингов нолями, также определеяется системой;
* $FILENAME -- любое имя файла от пользователя.

Пользовательская иерархия файлов не допускается by design. Недостатки подхода, по крайней мере, такие:

* имена файлов уже на существующей специализированной платформе для развёртывания приложения не поддерживают не-ASCII символы (в смысле от 0 до 31 и от 128 и выше кроме точки и косой черты);
* файл с таким же названием запросто может перетереть другой файл с таким же именем (или не дать создать другой файл с таким же именем, хотя содержимое будет отличаться, не суть);
* все файлы для одного пользователя по определённому типу лежат в одной директории, что может теоретически превысить лимить записей в директории, но что вряд ли когда-нибудь случится.

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

/blobs/$HASHED_FILE
/files/$TYPE/$TENANT_ID/$HASHED_FILENAME

где:

* $HASHED_FILE -- имя файла, составляемое из хеша содержимого этого же файла в виде строки в base16;
* $HASHED_FILENAME -- символическая ссылка в виде хеша имени оригинального файла в base16, узывающая на блоб из директории /blobs.

Все три (на самом деле, нет: кроме второй) вышеуказанные проблемы в обозримом будущем покрываются таким подходом. Так, пока невозможный в этой системе /files/backgrounds/162/пустой-файл.bin мог бы быть представлен на файловой системе следующим образом (пускай хеш-функцией будет SHA-1):

/blobs/da/39a3ee5e6b4b0d3255bfef95601890afd80709 -- пустой файл
/files/backgrounds/162/05/562e534a175beadd7c5c5504af329b02e2ffbb -- символическая ссылка на /blobs/da/39a3ee...709

Скрипты прямой миграции (но не обратной ввиду невозможности восстановить оригинальное имя файла из хеша) файлов со старой схемы реализуются просто.

Интересует: какие недостатки, кроме невозможности обратной миграции, есть у такого подхода + какие из уже существующих решений могут справиться с этой задачей?

Спасибо за советы.

------

EDIT1

Короче говоря, я многое упустил при постановке вопроса (есть реляционная БД, веб), поэтому эти недоговорки очень повлияли на ответы, все из которых полагают, что конечный пользователь видит файловую систему. Не видит, ему всё-равно, поэтому есть возможность переделать механизм работы хранилища как угодно. Ёщё путаницу привнесло то, что имена файлов, загружаемых пользователём, есть именами файлов прямо на файловой системе в хранилище --- вообще без валидации, трансформации и т.д. так как это изначально. Там сейчас просто хаос, от которого я хочу избавиться.

Мне очень жаль, что потратил ваше время на ответы на неполный вопрос.
Отредактировано 26.12.2019 13:13 halo . Предыдущая версия . Еще …
Отредактировано 26.12.2019 10:51 halo . Предыдущая версия .
Отредактировано 26.12.2019 10:46 halo . Предыдущая версия .
linux java
Re: Хранение файлов пользователей на файловой системе
От: vsb Казахстан  
Дата: 25.12.19 18:29
Оценка:
Почему бы просто не кодировать символы аналогично URL?
Re: Хранение файлов пользователей на файловой системе
От: L.K. Марс  
Дата: 25.12.19 18:36
Оценка:
H>Поскольку главной проблемой для заказчика пока является невозможность использовать не-ASCII символы в именах файлов

Что мешает поднять сервак без подробных ограничений?

И каждому пользователю выделить свою папку? Чтобы не бояться, что кто-то перепишет чужой файл.
http://mtdata.ru/u9/photoA8E5/20461312049-0/original.jpg
Re[2]: Хранение файлов пользователей на файловой системе
От: halo Украина  
Дата: 25.12.19 18:41
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>Почему бы просто не кодировать символы аналогично URL?


Если закрыть глаза на цену такого кодирования в виде возможной длины закодированного имени, это не избавляет от конфликта имён. Я забыл указать, что имена файлов не обязательно уникальны: в системе есть понятие приоритета (он уникален в рамках пары TYPE+TENANT_ID), но я бы не хотел вводить в хранилище понятие приоритета. Приоритет сейчас также не является понятием хранилища.
Re[2]: Хранение файлов пользователей на файловой системе
От: halo Украина  
Дата: 25.12.19 18:51
Оценка:
Здравствуйте, L.K., Вы писали:


H>>Поскольку главной проблемой для заказчика пока является невозможность использовать не-ASCII символы в именах файлов


LK>Что мешает поднять сервак без подробных ограничений?


1) Любые изменения на целевой платформе (это в этом случае -- специальный образ для виртуалок) требуют очень много времени для утверждения со стороны заказчика + я не знаю, насколько просто ему это будет сделать технически и выкатить позже. Зачастую такие глобальные изменения просто не допускаются, особенно если задача реализуема вообще в условиях текущей среды.

LK>И каждому пользователю выделить свою папку? Чтобы не бояться, что кто-то перепишет чужой файл.


2) TENANT_ID гарантирует, что пространства имён пользователей не пересекаются.
Re[3]: Хранение файлов пользователей на файловой системе
От: L.K. Марс  
Дата: 25.12.19 18:58
Оценка:
H>1) Любые изменения на целевой платформе (это в этом случае -- специальный образ для виртуалок)

А зачем на целевой платформе? Поднимается ещё одна виртуалка (файлохранилище), а для целевой платформы пишется небольшая служба (интерфейс для работы с файлами).

Ну или — как вариант — можно кодировать имена файлов в base58.
http://mtdata.ru/u9/photoA8E5/20461312049-0/original.jpg
Re[4]: Хранение файлов пользователей на файловой системе
От: halo Украина  
Дата: 25.12.19 19:09
Оценка:
Здравствуйте, L.K., Вы писали:

H>>1) Любые изменения на целевой платформе (это в этом случае -- специальный образ для виртуалок)


LK>А зачем на целевой платформе? Поднимается ещё одна виртуалка (файлохранилище), а для целевой платформы пишется небольшая служба (интерфейс для работы с файлами).


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

LK>Ну или — как вариант — можно кодировать имена файлов в base58.
Re[5]: Хранение файлов пользователей на файловой системе
От: L.K. Марс  
Дата: 25.12.19 19:25
Оценка:
H>Это и усложнение как таковое и вариант для сервиса с облаком и последующими миграциями в обеих случаях.

Это упрощение. Разделение логики и хранилища. Помогающее миграции.

Хотя руководство может полагать иначе, это да.
http://mtdata.ru/u9/photoA8E5/20461312049-0/original.jpg
Re: Хранение файлов пользователей на файловой системе
От: Cyberax Марс  
Дата: 25.12.19 20:41
Оценка:
Здравствуйте, halo, Вы писали:

H>Поскольку главной проблемой для заказчика пока является невозможность использовать не-ASCII символы в именах файлов, а не другие ограничения + я пока не рассматриваю другие подходы хранения файлов, можно переделать структуру директорий следующим образом:

Ну вообще, тут можно сделать так:
1) Хранить не-ASCII текст в именах файлов (закодировать в UTF-8). Любой Линукс это поддерживает.
2) Если не хочется файлов с "плохими" именами — закодировать в punycode ( https://en.wikipedia.org/wiki/Punycode ), который оставит ASCII-имена как есть.
Sapienti sat!
Re: Хранение файлов пользователей на файловой системе
От: Pzz Россия https://github.com/alexpevzner
Дата: 26.12.19 10:55
Оценка:
Здравствуйте, halo, Вы писали:

H>* $HASHED_FILE -- имя файла, составляемое из хеша содержимого этого же файла в виде строки в base16;

H>* $HASHED_FILENAME -- символическая ссылка в виде хеша имени оригинального файла в base16, узывающая на блоб из директории /blobs.

Как-то не очень понятно. Вот, пользователь создал файл по имени foo, для него подсчитался hash, создался blob и ссылка, все хорошо. Потом пользователь взял и перетер файл с именем foo новым содержимым, подсчитался новых hash, создался новый blob и ссылка. А если теперь пользователь захочет получить файл с именем foo, какой из двух ему отдадут, и какой смысл в хранении другого файла, если его не отдают?

Вообще, программы обычно пишутся для людей, а людям свойственно давать объектам, с которыми они взаимодействуют, человекочитаемые имена. Где и в каком виде они будут присутствовать в этой новой конструкции?
Re: Хранение файлов пользователей на файловой системе
От: Александр Кузнецов Россия  
Дата: 26.12.19 12:01
Оценка: +1
Здравствуйте, halo,

Имена файлов, как я понимаю судя по предложению с хешем, задаются не напрямую, а через какой-то интерфейс, то есть пользователь напрямую доступа к файлу не имеет.
Если так, то что мешает добавить небольшую базу (или добавить в имеющуюся базу поле) с соответствием: "человеко-читаемое имя" -> "ASCII имя" и просто делать трансформацию при вводе/выводе? Если уж совсем надо без базы, то можно в корень папки пользователя положить какой-нибудь файлик с жёстко заданным именем и в нём хранить такое соответствие.

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

В принципе, при этом можно и нюанс с перетиранием файлов с одинаковыми именами попробовать обойти, если добавить на UI ещё какой-нибудь признак (например, дату добавления файла) и показывать её одновременно с именем (чтобы юзер различал старые/новые файлы).
"Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете". (с) Макконнелл, "Совершенный код".
Re: Хранение файлов пользователей на файловой системе
От: XuMuK Россия  
Дата: 26.12.19 12:25
Оценка:
Здравствуйте, halo, Вы писали:

H>Пользовательская иерархия файлов не допускается by design. Недостатки подхода, по крайней мере, такие:


H>* имена файлов уже на существующей специализированной платформе для развёртывания приложения не поддерживают не-ASCII символы (в смысле от 0 до 31 и от 128 и выше кроме точки и косой черты);

H>* файл с таким же названием запросто может перетереть другой файл с таким же именем (или не дать создать другой файл с таким же именем, хотя содержимое будет отличаться, не суть);
H>* все файлы для одного пользователя по определённому типу лежат в одной директории, что может теоретически превысить лимить записей в директории, но что вряд ли когда-нибудь случится.

H>/blobs/$HASHED_FILE

H>/files/$TYPE/$TENANT_ID/$HASHED_FILENAME

H>* $HASHED_FILE -- имя файла, составляемое из хеша содержимого этого же файла в виде строки в base16;

H>* $HASHED_FILENAME -- символическая ссылка в виде хеша имени оригинального файла в base16, узывающая на блоб из директории /blobs.

Как уже советовали выше для имени файла можно использовать любую обратимую ascii кодировку, а для решения второй проблемы составное имя ссылки вида "<encoded-filename><split-symbol><content-hash>", split-symbol выбрать так чтобы его не могло быть в закодированном имени файла и в хеше.

Потенциальных проблем по сравнению со старой системой как минимум две:
* Наладить взаимодействие пользователя и файлов с одинаковыми именами.
* Отсутствие сортировки по умолчанию. Для того чтобы показать пользователю файлы с 100 по 199 придется прочитать всю директорию и перекодировать имена.
Re[2]: Хранение файлов пользователей на файловой системе
От: halo Украина  
Дата: 26.12.19 12:51
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Здравствуйте, halo, Вы писали:


H>>* $HASHED_FILE -- имя файла, составляемое из хеша содержимого этого же файла в виде строки в base16;

H>>* $HASHED_FILENAME -- символическая ссылка в виде хеша имени оригинального файла в base16, узывающая на блоб из директории /blobs.

Pzz>Как-то не очень понятно. Вот, пользователь создал файл по имени foo, для него подсчитался hash, создался blob и ссылка, все хорошо. Потом пользователь взял и перетер файл с именем foo новым содержимым, подсчитался новых hash, создался новый blob и ссылка. А если теперь пользователь захочет получить файл с именем foo, какой из двух ему отдадут, и какой смысл в хранении другого файла, если его не отдают?


Угу, я поспешил со вторым пунктом. Поправил этот момент в вопросе одновременно с этим ответом. Вариант: симлинк на блоб в промежуточной поддиректории. Не совсем хорошо.

Pzz>Вообще, программы обычно пишутся для людей, а людям свойственно давать объектам, с которыми они взаимодействуют, человекочитаемые имена. Где и в каком виде они будут присутствовать в этой новой конструкции?


И здесь поспешил: не указал, что есть БД, веб-интерфейс, и конечному пользователю плевать, как что и где лежит.

Вобщем, это всё не важно.
Re[3]: Хранение файлов пользователей на файловой системе
От: Pzz Россия https://github.com/alexpevzner
Дата: 26.12.19 15:56
Оценка:
Здравствуйте, halo, Вы писали:

H>И здесь поспешил: не указал, что есть БД, веб-интерфейс, и конечному пользователю плевать, как что и где лежит.


Я пытаюсь намекнуть, что у этих "файлов" есть какая-то первичная идентификация, по которой люди их различают. Может, выбирая имена дисковых файлов, стоит от нее отталкиваться?
Re: Хранение файлов пользователей на файловой системе
От: Masterspline  
Дата: 26.12.19 19:12
Оценка: 6 (1)
Я бы сделал таблицу в sqlite с полями:

$TYPE, $TENANT_ID, $FILENAME, $FILENAME_HASH

и хранил бы файлы в виде

/blobs/xx/yy/$FILENAME_HASH, где xx и yy — первая и вторая пара символов в хеше (или просто /blobs/$FILENAME_HASH, если файлов не много).

Как вариант, можно существующие файлы хранить по старой схеме, а новые по-новому.

Upd.
Вместо $FILENAME_HASH можно использовать хеш от содержимого файла. Для многопоточки при добавлении и удалении файлов — лочить папку или делать блокировку в базе.
Отредактировано 27.12.2019 17:08 Ssd13 . Предыдущая версия .
Re: Хранение файлов пользователей на файловой системе
От: Somescout  
Дата: 26.12.19 19:38
Оценка: 4 (1)
Здравствуйте, halo, Вы писали:

H>/blobs/$HASHED_FILE

H>/files/$TYPE/$TENANT_ID/$HASHED_FILENAME

H>где:


H>* $HASHED_FILE -- имя файла, составляемое из хеша содержимого этого же файла в виде строки в base16;

H>* $HASHED_FILENAME -- символическая ссылка в виде хеша имени оригинального файла в base16, узывающая на блоб из директории /blobs.

H>Все три (на самом деле, нет: кроме второй) вышеуказанные проблемы в обозримом будущем покрываются таким подходом. Так, пока невозможный в этой системе /files/backgrounds/162/пустой-файл.bin мог бы быть представлен на файловой системе следующим образом (пускай хеш-функцией будет SHA-1):


H>/blobs/da/39a3ee5e6b4b0d3255bfef95601890afd80709 -- пустой файл

H>/files/backgrounds/162/05/562e534a175beadd7c5c5504af329b02e2ffbb -- символическая ссылка на /blobs/da/39a3ee...709

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

Если не планируется большой выигрыш от дедубликации файлов (оценить это можно уже сейчас), я бы убрал отдельное хранение блобов.
Если оно всё-же нужно, то стоит внести дополнительный слой — экземпляры файлов с хардлинками на исходник (примерно так делает dovecot):

/blobs/$HASHED_FILE-$TENANT_ID -> (hardlink to) -> /blobs/hashes/$HASHED_FILE
/files/$TYPE/$TENANT_ID/$HASHED_FILENAME -> (softlink to) -> /blobs/$HASHED_FILE-$TENANT_ID

Тогда алгоритм удаления будет таким: снести ссылку в каталоге пользователя, хардлинк по этой ссылке и если больше хардлинков не осталось, снести сам блоб (и как-то избежать racing condition).
ARI ARI ARI... Arrivederci!
Отредактировано 26.12.2019 19:39 Somescout . Предыдущая версия .
Re[2]: Хранение файлов пользователей на файловой системе
От: Somescout  
Дата: 26.12.19 20:02
Оценка:
Здравствуйте, Somescout, Вы писали:


S>/blobs/$HASHED_FILE-$TENANT_ID -> (hardlink to) -> /blobs/hashes/$HASHED_FILE

S>/files/$TYPE/$TENANT_ID/$HASHED_FILENAME -> (softlink to) -> /blobs/$HASHED_FILE-$TENANT_ID

Поправлю себя:
Тут должен быть не "$HASHED_FILE-$TENANT_ID", а что-то более уникальное, в худшем случае (если нет ничего короче): "$HASHED_FILE-$TYPE-$TENANT_ID-$HASHED_FILENAME".
ARI ARI ARI... Arrivederci!
Re[2]: Хранение файлов пользователей на файловой системе
От: Sharov Россия  
Дата: 27.12.19 13:38
Оценка:
Здравствуйте, Masterspline, Вы писали:

M>/blobs/xx/yy/$FILENAME_HASH, где xx и yy — первая и вторая пара символов в хеше (или просто /blobs/$FILENAME_HASH, если файлов не много).


А откуда вообще взялось такое именование путей, где xx и yy — первая и вторая пара символов в хеше ? Не в первый раз встречаю, а откуда исток понять не могу.
Почему две пары а не три или четыре (xx/yy/zz..)?
Кодом людям нужно помогать!
Re[3]: Хранение файлов пользователей на файловой системе
От: Masterspline  
Дата: 27.12.19 16:52
Оценка: 14 (2)
S>А откуда вообще взялось такое именование путей, где xx и yy — первая и вторая пара символов в хеше ? Не в первый раз встречаю, а откуда исток понять не могу.
S>Почему две пары а не три или четыре (xx/yy/zz..)?

Раньше на Linux файловая система (ext3 и более ранние версии ext) была тормозной на добавление и удаление файлов из директории с большим количеством файлов (поиск, вроде был достаточно быстрым). Поэтому придумали способ уменьшения количества файлов в папке (пройти по двум уровням вложенности было быстрее, чем удалять файлы в папке, где их 10`000`000, например). Вложенность дерева зависит от количества файлов (двух уровней хватит всем, а многим и одного достаточно). Сейчас это, возможно, уже не актуально (там что-то поправили, вроде).
Re[4]: Хранение файлов пользователей на файловой системе
От: akasoft Россия  
Дата: 28.12.19 08:39
Оценка: +1
Здравствуйте, Masterspline, Вы писали:

M>Раньше на Linux файловая система (ext3 и более ранние версии ext) была тормозной на добавление и удаление файлов из директории с большим количеством файлов


Это и на Windows так. Попробуй поработать с загаженым %temp%.
Т.е. разбивание папки хранения на несколько частей-подпапок, это нормальный подход на файловой системе, если файлов более 1000.
Вопрос — почему берутся первые пары символов хеша? Они что, гарантируют равномерное распределение файлов по подпапкам?
Ведь по сути, чтобы не было деградации производительности, нужно хранить в папке не более 500 файлов, и если количество достигло, переключать хранение на др. папку.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.