В этом задании вам предстоит реализовать собственное
персистентное key-value хранилище. Про такие системы можно
интуитивно думать как про Map
, которая сохраняет информацию на диск. key-value
хранилища используются повсеместно – от системы индексов в вашей любимой IDE до хранения фотографий в некоторых
социальных сетях.
Наша система будет состоять из следующих частей:
KeyValueStore
- класс для взаимодействия с базой данных, управляетIndexManager
иValueStoreManager
IndexManager
- сущность, которая занимается чтением/записью ключей на диск и поддержанием связиkey-value
ValueStoreManager
- сущность, которая занимается чтением/записью значений на диск
На систему накладываются следующие ограничения:
-
Значения хранятся в файлах ограниченного размера (максимальный размер файла передается в
KeyValueStoreFactory#create
). Если при записи значения закончился текущий файл, необходимо создать новый и продолжить записывать в него. Указатели на эти блоки значений описываются с помощью дескрипторовFileBlockLocation
.Важные детали:
-
Одно значение может быть разбито на несколько кусков, которые хранятся в разных файлах.
Например, это нужно, когда размер значения больше, чем максимальный размер одного файла. В этом случае его приходится разбивать на куски.
-
В одном файле может храниться сразу несколько значений или их кусков.
-
-
IndexManager
инициализирует и поддерживает индексный файл, который хранит связиkey -> List<FileBlockLocation>
-
Для упрощения задачи в базе не будет честной физической операции удаления. Удаление будет только логическим - для этого достаточно удалить соответствующую связь в
IndexManager
и пометить занятые значением блоки как свободные ( список свободных блоков проще всего поддерживать внутриValueStoreManager
с помощью дополнительного индексного файла) -
В дальнейшем, для экономии памяти, значения должны сначала записываться в свободные блоки. Новые файлы должны создаваться только в случае, когда свободных блоков не осталось.
Скорее всего, для реализации понадобится RandomAccessFile
. Посмотрите, какие методы у него есть и как с ним работать
При инициализации пустой базы IndexManager
должен создавать индексный файл в рабочей директории (она передается при
инициализации системы через KeyValueStoreFactory#create
). Индексный файл должен обновляться свежей информацией как
минимум при вызове #close
.
Главная задача ValueStoreManager
- писать и читать значения из файлов. Для этого ему необходимо поддерживать
информацию о свободных блоках в файлах. При удалении по ключу занятые блоки должны перемещаться в пул свободных блоков.
При записи нужно брать свободные блоки и писать сначала в них, а потом дописывать в новые файлы. После того как
очередное значение было записано, нужно добавить соответствующую запись в индекс. Для этого ValueStoreManager#add
возвращает набор записанных блоков, которые нужно передать в IndexManager
.
Для удобства чтения из блока нужно написать собственную имплементацию InputStream
, которая будет уметь читать один
блок. Значение может быть разбито на несколько блоков - в этом случае можно открыть по потоку для каждого из них, а
затем объединить их в один поток, используя класс java.io.SequenceInputStream
.
Все запчасти готовы, осталось собрать:)
P.S. Стоит внимательно подумать про работу с эксепшенами.
При создании вашего репозитория будет автоматически создан Pull Request для проверки .
Вы должны закоммитить своё решение в ветку main
(это ветка вашего репозитория по-умолчанию), и эти коммиты
автоматически добавятся в Pull Request.
Этот пулл-реквест не нужно мерджить и не нужно закрывать!!!
Когда вы будете готовы к первой проверке, сделайте следующее:
- Добавьте к этому пулл-реквесту лейбл
ready-for-review
. Это можно сделать в правой части страницы с пулл-реквестом в менюLabels
. - Отправьте ссылку на этот пулл-реквест в качестве решения на сайте CSC.
В дальнейшем, если вы внесли исправления и хотите запросить очередную проверку, просто повторно запросите ревью от преподавателя, который вас уже проверял:
Если у вас возникают проблемы на каком-то из этих шагов, пожалуйста, сообщите об этом в Slack. Чем быстрее вы это сделаете, тем быстрее мы поможем вам.