Создание отказоустойчивого генератора серийных номеров с помощью файлов журнала

У меня есть система, которая в основном является генератором серийных номеров.

Существует два основных требования к конструкции генератора серийных номеров (с точки зрения отказоустойчивости):

  1. Все серийные номера должны быть уникальными (без дубликатов)
  2. Если генератор серийных номеров дает сбой, ему нужен способ приблизительно перезапустить с того места, где он остановился (пропуск нескольких серийных номеров при восстановлении после сбоя - это нормально, если необходимо, чтобы убедиться, что требование № 1 выполнено)

Можно предположить, что серийные номера выдаются последовательно (1, 2, 3, 4 и т. Д.)

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

Вот проблема:

Итак, каковы плюсы / минусы следующих подходов регистрации:

  1. Имейте единственный файл журнала, который добавлен, и ограничен в определенном размере
  2. Есть два файла журнала, ни один из которых не добавляется. Первый файл журнала всегда содержит последний выпущенный серийный номер, а второй файл содержит второй последний выпущенный серийный номер. Файлы журналов записываются в виде наглядности (т.е. все четные серийные номера попадают в файл журнала "A", а нечетные серийные номера помещаются в файл журнала "B"). Конечный результат заключается в том, что у вас всегда есть последние два выпущенных серийных номера (и не более), использование дискового пространства незначительно, и если произошел сбой генератора серийного номера при регистрации самого последнего серийного номера, то ' самый последний 'файл может быть поврежден. Поскольку у нас также есть второй самый последний серийный номер в другом файле журнала, мы можем обнаружить сбой и перезапустить генератор серийного номера с этого "второго самого последнего" серийного номера, +2

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

Вариант 1 немного проще для реализации, но вариант 2 использует меньше дискового пространства и кажется более интеллектуальным.

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

2 ответа

Решение

Вам нужно определиться с царством "закрыть". Под этим я подразумеваю, сколько цифр вы готовы потерять в случае аварии.

Скажем, его 1000.

Затем вы сохраняете самую большую последовательность в файле.

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

Если произошел сбой, вам нужно ОСТАНОВИТЬ и прервать генератор последовательности.

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

Итак, вот процедура.

function int getNextSequence() {
    currentSeq = currentSeq + 1; 
    if (currentSeq >= maxSeq) {
        maxSeq = maxSeq + 1000;
        write(maxSeq, "newSeq");
        rename("newSeq", "curSeq");
    }
    return currentSeq;
}

function restartSequence() {
    maxSeq = read("curSeq");

    currentSeq = maxSeq - 1; // This will immediately create a disk update on first use.
 }

Здесь может быть одна ошибка, не проверенная.

Дополнения:

Если вы так волнуетесь, вы можете сохранить четыре фрагмента данных в памяти и выписать две копии. Или лучше шесть и три.

Данные, которые вы храните в памяти, представляют собой три копии счетчика и три контрольные суммы этих счетчиков (возможно, MD5 значения).

Затем, когда вы пишете их, вы используете ту же технику, что и выше, пишите, а затем переименовываете.

Но вы пишете значения и хэши.

Причина, по которой вы это делаете, проста.

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

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

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

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

Вдобавок ко всему, некоторые из них могут быть: нехватка места на диске, неаккуратное кодирование со свободными ресурсами, проблемы с потоками и т. Д.

Если ваша цель состоит в том, чтобы просто убедиться, что сгенерированный серийный номер сохранен и уникален, то я, вероятно, рекомендую использовать что-то вроде сервера sql в сочетании со столбцом типа NEWSEQUENTIALID(). Здесь есть определенные преимущества из-за проблемного пространства, которое SQL Server уже решил. Количество транзакций в секунду, которые вы можете поддерживать, зависит от оборудования и от того, как вы его используете.

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

Например. Если у вас возникли проблемы с потоками, рассмотрите возможность использования веб-сервера для решения всего этого за вас. Если у вас проблемы с дисковым пространством, рассмотрите возможность обновления вашего оборудования. Если у вас есть проблемы с обеспечением устойчивости, используйте сервер SQL (бренд не имеет большого значения) для хранения данных. Если генераторная машина перегружена, рассмотрите другую архитектуру, которая позволяет разумно распределять нагрузку на устройства.


Еще одна вещь: я думаю, что ни один из описанных вами подходов не является хорошим решением. Если вы действительно генерируете 1000 в секунду, то вы можете рассмотреть балансировку нагрузки генерации. В этот момент у вас возникнут серьезные проблемы с выяснением того, как поддерживать регулярные файлы журналов синхронизированными между несколькими точками генерации... Какой сервер SQL уже хорош.

Другие вопросы по тегам