Как реализовать ACID-транзакции в пользовательском хранилище ключей и значений

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

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

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

Я могу легко превратить эту файловую систему в собственное хранилище ключей и значений, поэтому мне действительно нужна некоторая библиотека C#/.NET, которая добавляет транзакции в произвольное хранилище значений ключей. Мне не нужна производительность в стиле Facebook/Google, хранилище ключ-значение будет использоваться для экономии только десятков ГБ.


Предположим, удаленное хранилище ключей и значений позволяет мне использовать string get(id), void put(id, string) и delete(id) записей. Никакая другая информация не может быть получена с сервера (даже при ошибках не создаются исключения). Учитывая, что единственными клиентами, имеющими доступ к хранилищу, будут мои, как мне реализовать их так, чтобы только один мог одновременно писать/удалять (блокировка записи)? Учитывая очень-очень ограниченный серверный интерфейс, возможно ли это вообще? Я предполагаю, что решение потребует очень хорошего криптографического трюка, но я пока не смог придумать ничего, что сработало бы.


Ресурсы


person Paya    schedule 30.12.2014    source источник
comment
Как вы можете обеспечить ACID(ity) данных, хранящихся на сервере, с клиентской стороны?   -  person codelion    schedule 30.12.2014
comment
@codelion это то, что я хочу выяснить ... может быть способ реализовать какую-то систему обмена сообщениями в этом хранилище ключ-значение, чтобы клиенты могли синхронизироваться (и реализовывать блокировки с помощью тайм-аутов и т. д.), но я этого не делаю хочу заново изобрести велосипед   -  person Paya    schedule 30.12.2014
comment
Как могут помочь блокировки, если сама базовая файловая система не гарантирует свойства ACID?   -  person codelion    schedule 30.12.2014
comment
ну, это может помочь реализовать атомарность ... и, как следует из моего поста, у меня те же вопросы, что и у вас, и я ищу решение. Вот о чем мой пост.   -  person Paya    schedule 30.12.2014
comment
Просто в качестве уточнения, я контролирую исходный код всех клиентов. Таким образом, при разумном подходе может существовать способ сделать хранилище ACID с точки зрения клиента, поскольку все клиенты могут следовать одному и тому же протоколу.   -  person Paya    schedule 30.12.2014
comment
Является ли хранилище, которое вы сохраняете, файловой системой NTFS или другой файловой системой, имеющей перехватчики, которые можно вызывать из System.Transactions.Transaction?   -  person Scott Chamberlain    schedule 04.01.2015
comment
@ScottChamberlain К сожалению, нет :-( Это удаленная служба RESTful.   -  person Paya    schedule 04.01.2015
comment
Есть ли в службе операция move или rename, которая может перезаписать существующий файл, при этом конечный файл никогда не будет находиться в несогласованном состоянии? Также вы можете получить метаданные Время последней записи для файлов?   -  person Scott Chamberlain    schedule 04.01.2015
comment
@ScottChamberlain К счастью, с моим текущим хранилищем я могу это сделать, но API хранилища нестабилен, и гарантируется только сокращенный API get-content/put-content/delete. Однако из любопытства, как бы вы реализовали блокировку с move и last write time? (Учитывая непредсказуемую задержку сети.)   -  person Paya    schedule 05.01.2015


Ответы (1)


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

Когда клиент завершает изменение скопированной версии файла, он переименовывает файл обратно в исходное «незаблокированное» имя файла, перезаписывая неизмененную исходную копию в одной атомарной операции.

Это дает вам все, что требуется для ACID, за исключением Dдолговечности, поскольку оставленная блокировка определяется как оставленная только через некоторое время.

Если у вас не было доступа к времени последней записи из файлов, вы все равно могли бы использовать эту структуру, но вам нужно было бы добавить 3-й файл «метаданных», чтобы представить блокировку вместо вашей «текущей» копии файла. Этот файл метаданных будет просто содержать метку времени создания блокировки в качестве своего содержимого.

person Scott Chamberlain    schedule 05.01.2015
comment
Но это работает только в том случае, если метод copy не работает, если целевой файл уже существует. Если это не сработает, тогда 2 или более клиентов могут думать, что у них есть блокировка одновременно, верно? И в случае, когда я использую уникальное имя файла назначения (для операции копирования) для каждого клиента, тогда, если несколько клиентов пытаются получить блокировку одновременно, тогда 1 или более клиентов могут преуспеть (из-за временной задержки между проверкой для файл блокировки и копирование). - person Paya; 05.01.2015
comment
Ты прав. Вам потребуется уникальное имя файла для каждого клиента. Процедура получения блокировки будет следующей: «Проверить», «Создать блокировку», «Проверить одновременную блокировку», «откатить», если одновременная блокировка будет выполнена, и повторить попытку через случайный интервал. - person Scott Chamberlain; 05.01.2015
comment
Спасибо, кажется, это работает во всех случаях, о которых я могу думать (за исключением взаимоблокировок, вызванных случайными интервалами...). Однако этот подход требует, чтобы клиенты знали друг друга заранее. В случае, если они не знают друг друга заранее, и они могут прийти в любое время и исчезнуть без уведомления в любое время, как вы надежно опубликуете и отмените публикацию (тайм-аут) их присутствия? - person Paya; 05.01.2015
comment
Я не понимаю, в чем заключается необходимость того, чтобы клиенты знали друг о друге. Формат блокировки может быть OriginalFileName.ext.{Client Guid}.lck все, что нужно сделать клиенту, это пройти имя файла в обратном порядке. Проверьте, есть ли *.lck, затем проверьте, является ли guid клиента собственным guid клиента. Если это его собственный guid, он может игнорировать его, если это чужой guid, он проверяет возраст файла по тайм-ауту, чтобы увидеть, снята ли блокировка. Если блокировка отменена, она удаляет блокировку, а затем перезапускает процесс с самого начала. - person Scott Chamberlain; 05.01.2015
comment
Ну ладно, если предположить, что есть функция вроде GetFirstFile/GetNextFile или что-то в этом роде, для вывода списка файлов... Я думал среди строк интерфейса get_content, set_content и delete без всяких get_ids... - person Paya; 05.01.2015