Работаю над старым проектом, в котором пользовательские файлы хранятся прямо на файловой системе. Схема размещения файлов подчиняется следующему шаблону:
/files/$TYPE/$TENANT_ID/$FILENAME
где:
* $TYPE -- предназначение файлов, жёстко определяемое системой;
* $TENANT_ID -- код пользователя, обычная целочисленная строка без паддингов нолями, также определеяется системой;
* $FILENAME -- любое имя файла от пользователя.
Пользовательская иерархия файлов не допускается by design. Недостатки подхода, по крайней мере, такие:
* имена файлов уже на существующей специализированной платформе для развёртывания приложения не поддерживают не-ASCII символы (в смысле от 0 до 31 и от 128 и выше кроме точки и косой черты);
* файл с таким же названием запросто может перетереть другой файл с таким же именем (или не дать создать другой файл с таким же именем, хотя содержимое будет отличаться, не суть);
* все файлы для одного пользователя по определённому типу лежат в одной директории, что может теоретически превысить лимить записей в директории, но что вряд ли когда-нибудь случится.
Поскольку главной проблемой для заказчика пока является невозможность использовать не-ASCII символы в именах файлов, а не другие ограничения + я пока не рассматриваю другие подходы хранения файлов, можно переделать структуру директорий следующим образом:
* $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
Короче говоря, я многое упустил при постановке вопроса (есть реляционная БД, веб), поэтому эти недоговорки очень повлияли на ответы, все из которых полагают, что конечный пользователь видит файловую систему. Не видит, ему всё-равно, поэтому есть возможность переделать механизм работы хранилища как угодно. Ёщё путаницу привнесло то, что имена файлов, загружаемых пользователём, есть именами файлов прямо на файловой системе в хранилище --- вообще без валидации, трансформации и т.д. так как это изначально. Там сейчас просто хаос, от которого я хочу избавиться.
Мне очень жаль, что потратил ваше время на ответы на неполный вопрос.
Здравствуйте, vsb, Вы писали:
vsb>Почему бы просто не кодировать символы аналогично URL?
Если закрыть глаза на цену такого кодирования в виде возможной длины закодированного имени, это не избавляет от конфликта имён. Я забыл указать, что имена файлов не обязательно уникальны: в системе есть понятие приоритета (он уникален в рамках пары TYPE+TENANT_ID), но я бы не хотел вводить в хранилище понятие приоритета. Приоритет сейчас также не является понятием хранилища.
Re[2]: Хранение файлов пользователей на файловой системе
H>>Поскольку главной проблемой для заказчика пока является невозможность использовать не-ASCII символы в именах файлов
LK>Что мешает поднять сервак без подробных ограничений?
1) Любые изменения на целевой платформе (это в этом случае -- специальный образ для виртуалок) требуют очень много времени для утверждения со стороны заказчика + я не знаю, насколько просто ему это будет сделать технически и выкатить позже. Зачастую такие глобальные изменения просто не допускаются, особенно если задача реализуема вообще в условиях текущей среды.
LK>И каждому пользователю выделить свою папку? Чтобы не бояться, что кто-то перепишет чужой файл.
2) TENANT_ID гарантирует, что пространства имён пользователей не пересекаются.
Re[3]: Хранение файлов пользователей на файловой системе
H>1) Любые изменения на целевой платформе (это в этом случае -- специальный образ для виртуалок)
А зачем на целевой платформе? Поднимается ещё одна виртуалка (файлохранилище), а для целевой платформы пишется небольшая служба (интерфейс для работы с файлами).
Ну или — как вариант — можно кодировать имена файлов в base58.
Re[4]: Хранение файлов пользователей на файловой системе
Здравствуйте, L.K., Вы писали:
H>>1) Любые изменения на целевой платформе (это в этом случае -- специальный образ для виртуалок)
LK>А зачем на целевой платформе? Поднимается ещё одна виртуалка (файлохранилище), а для целевой платформы пишется небольшая служба (интерфейс для работы с файлами).
Это и усложнение как таковое и вариант для сервиса с облаком и последующими миграциями в обеих случаях. Я почти уверен, что ни тот ни другой вариант не пропустят. Как максимум -- соответствующая решению проблемы служба и хранилище на самой платформе. Но снова же: нужно будет убеждать, что переход будет простым и не требующим сложных изменений, иначе просто завернут.
LK>Ну или — как вариант — можно кодировать имена файлов в base58.
Re[5]: Хранение файлов пользователей на файловой системе
Здравствуйте, halo, Вы писали:
H>Поскольку главной проблемой для заказчика пока является невозможность использовать не-ASCII символы в именах файлов, а не другие ограничения + я пока не рассматриваю другие подходы хранения файлов, можно переделать структуру директорий следующим образом:
Ну вообще, тут можно сделать так:
1) Хранить не-ASCII текст в именах файлов (закодировать в UTF-8). Любой Линукс это поддерживает.
2) Если не хочется файлов с "плохими" именами — закодировать в punycode ( https://en.wikipedia.org/wiki/Punycode ), который оставит ASCII-имена как есть.
Sapienti sat!
Re: Хранение файлов пользователей на файловой системе
Здравствуйте, halo, Вы писали:
H>* $HASHED_FILE -- имя файла, составляемое из хеша содержимого этого же файла в виде строки в base16; H>* $HASHED_FILENAME -- символическая ссылка в виде хеша имени оригинального файла в base16, узывающая на блоб из директории /blobs.
Как-то не очень понятно. Вот, пользователь создал файл по имени foo, для него подсчитался hash, создался blob и ссылка, все хорошо. Потом пользователь взял и перетер файл с именем foo новым содержимым, подсчитался новых hash, создался новый blob и ссылка. А если теперь пользователь захочет получить файл с именем foo, какой из двух ему отдадут, и какой смысл в хранении другого файла, если его не отдают?
Вообще, программы обычно пишутся для людей, а людям свойственно давать объектам, с которыми они взаимодействуют, человекочитаемые имена. Где и в каком виде они будут присутствовать в этой новой конструкции?
Re: Хранение файлов пользователей на файловой системе
Имена файлов, как я понимаю судя по предложению с хешем, задаются не напрямую, а через какой-то интерфейс, то есть пользователь напрямую доступа к файлу не имеет.
Если так, то что мешает добавить небольшую базу (или добавить в имеющуюся базу поле) с соответствием: "человеко-читаемое имя" -> "ASCII имя" и просто делать трансформацию при вводе/выводе? Если уж совсем надо без базы, то можно в корень папки пользователя положить какой-нибудь файлик с жёстко заданным именем и в нём хранить такое соответствие.
Тогда пользователь видит/работает с человеко-читаемыми именами, а имя в файловой системе никому, кроме самой системы, не интересно, и может быть любым, хоть номером файла в каталоге по дате добавления.
В принципе, при этом можно и нюанс с перетиранием файлов с одинаковыми именами попробовать обойти, если добавить на UI ещё какой-нибудь признак (например, дату добавления файла) и показывать её одновременно с именем (чтобы юзер различал старые/новые файлы).
"Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете". (с) Макконнелл, "Совершенный код".
Re: Хранение файлов пользователей на файловой системе
Здравствуйте, 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]: Хранение файлов пользователей на файловой системе
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, halo, Вы писали:
H>>* $HASHED_FILE -- имя файла, составляемое из хеша содержимого этого же файла в виде строки в base16; H>>* $HASHED_FILENAME -- символическая ссылка в виде хеша имени оригинального файла в base16, узывающая на блоб из директории /blobs.
Pzz>Как-то не очень понятно. Вот, пользователь создал файл по имени foo, для него подсчитался hash, создался blob и ссылка, все хорошо. Потом пользователь взял и перетер файл с именем foo новым содержимым, подсчитался новых hash, создался новый blob и ссылка. А если теперь пользователь захочет получить файл с именем foo, какой из двух ему отдадут, и какой смысл в хранении другого файла, если его не отдают?
Угу, я поспешил со вторым пунктом. Поправил этот момент в вопросе одновременно с этим ответом. Вариант: симлинк на блоб в промежуточной поддиректории. Не совсем хорошо.
Pzz>Вообще, программы обычно пишутся для людей, а людям свойственно давать объектам, с которыми они взаимодействуют, человекочитаемые имена. Где и в каком виде они будут присутствовать в этой новой конструкции?
И здесь поспешил: не указал, что есть БД, веб-интерфейс, и конечному пользователю плевать, как что и где лежит.
Вобщем, это всё не важно.
Re[3]: Хранение файлов пользователей на файловой системе
Здравствуйте, halo, Вы писали:
H>И здесь поспешил: не указал, что есть БД, веб-интерфейс, и конечному пользователю плевать, как что и где лежит.
Я пытаюсь намекнуть, что у этих "файлов" есть какая-то первичная идентификация, по которой люди их различают. Может, выбирая имена дисковых файлов, стоит от нее отталкиваться?
Re: Хранение файлов пользователей на файловой системе
/blobs/xx/yy/$FILENAME_HASH, где xx и yy — первая и вторая пара символов в хеше (или просто /blobs/$FILENAME_HASH, если файлов не много).
Как вариант, можно существующие файлы хранить по старой схеме, а новые по-новому.
Upd.
Вместо $FILENAME_HASH можно использовать хеш от содержимого файла. Для многопоточки при добавлении и удалении файлов — лочить папку или делать блокировку в базе.
Здравствуйте, 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):
Тогда алгоритм удаления будет таким: снести ссылку в каталоге пользователя, хардлинк по этой ссылке и если больше хардлинков не осталось, снести сам блоб (и как-то избежать racing condition).
Поправлю себя:
Тут должен быть не "$HASHED_FILE-$TENANT_ID", а что-то более уникальное, в худшем случае (если нет ничего короче): "$HASHED_FILE-$TYPE-$TENANT_ID-$HASHED_FILENAME".
ARI ARI ARI... Arrivederci!
Re[2]: Хранение файлов пользователей на файловой системе
Здравствуйте, Masterspline, Вы писали:
M>/blobs/xx/yy/$FILENAME_HASH, где xx и yy — первая и вторая пара символов в хеше (или просто /blobs/$FILENAME_HASH, если файлов не много).
А откуда вообще взялось такое именование путей, где xx и yy — первая и вторая пара символов в хеше ? Не в первый раз встречаю, а откуда исток понять не могу.
Почему две пары а не три или четыре (xx/yy/zz..)?
Кодом людям нужно помогать!
Re[3]: Хранение файлов пользователей на файловой системе
S>А откуда вообще взялось такое именование путей, где xx и yy — первая и вторая пара символов в хеше ? Не в первый раз встречаю, а откуда исток понять не могу. S>Почему две пары а не три или четыре (xx/yy/zz..)?
Раньше на Linux файловая система (ext3 и более ранние версии ext) была тормозной на добавление и удаление файлов из директории с большим количеством файлов (поиск, вроде был достаточно быстрым). Поэтому придумали способ уменьшения количества файлов в папке (пройти по двум уровням вложенности было быстрее, чем удалять файлы в папке, где их 10`000`000, например). Вложенность дерева зависит от количества файлов (двух уровней хватит всем, а многим и одного достаточно). Сейчас это, возможно, уже не актуально (там что-то поправили, вроде).
Re[4]: Хранение файлов пользователей на файловой системе
Здравствуйте, Masterspline, Вы писали:
M>Раньше на Linux файловая система (ext3 и более ранние версии ext) была тормозной на добавление и удаление файлов из директории с большим количеством файлов
Это и на Windows так. Попробуй поработать с загаженым %temp%.
Т.е. разбивание папки хранения на несколько частей-подпапок, это нормальный подход на файловой системе, если файлов более 1000.
Вопрос — почему берутся первые пары символов хеша? Они что, гарантируют равномерное распределение файлов по подпапкам?
Ведь по сути, чтобы не было деградации производительности, нужно хранить в папке не более 500 файлов, и если количество достигло, переключать хранение на др. папку.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[4]: Хранение файлов пользователей на файловой системе