Как сделать атомарную работу с файловой системой и базой данных в Postgres? - PullRequest
0 голосов
/ 23 января 2019

Я думаю, что следующий шаблон должен быть довольно распространенным:

  • База данных используется для хранения путей к файлам
  • Сами файлы хранятся в файловой системе

Могут возникнуть проблемы, когда, скажем, мы хотим изменить путь к файлу: нам нужно оба изменить путь к файлу базы данных и переместить файл в файловой системе. Важно, что это делается «атомарно». Действительно, пока мы выполняем модификацию, другой процесс может попытаться прочитать путь к файлу в базе данных, а затем попытается получить доступ к файлу в файловой системе. Мы должны убедиться, что кортеж

("путь к файлу", "фактическое местоположение файла")

остается постоянным все время.

Есть ли канонический / простой способ добиться этого с Postgres / Linux?

1 Ответ

0 голосов
/ 23 января 2019

Одной из основных особенностей базы данных является то, что процессы видят ее последовательно. Это также означает, что разные клиенты видят разные состояния базы данных.

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

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

Я бы попробовал реализовать следующую схему (псевдокод):

sql("begin")
os.hardlink(old_path, new_path)
sql("update files set path=? where path=?, new_path, old_path)
sql("insert into files_to_clean values (?, txid_current())", old_path)
sql("commit")

if random()<CLEANUP_PROBABILITY:
  sql("begin")
  for delete_path in sql("
    delete from files_to_clean
    where txid<txid_snapshot_xmin(txid_current_snapshot())
    returning path skip locked
  "):
    os.delete(delete_path)
  sql("commit")
...