Сервис читает из очереди (Kafka или аналог) версии веб-страниц и пишет в другую
очередь склеенную запись по каждому документу. Транспорт уже написан: он
вызывает Process на каждое сообщение и отправляет результат дальше, а если
вернулся nil — ничего не пишет. От тебя нужна бизнес-логика.
type Page struct {
URL string
PublishedAt uint64
SeenAt uint64 // когда сервис получил эту версию; можно считать номером версии
Body string
FirstSeenAt uint64 // во входных сообщениях не заполнено, вычисляешь ты
}
var ErrEmptyURL = errors.New("empty URL")
func NewIngestor() *Ingestor
func (in *Ingestor) Process(p Page) (*Page, error)
URL идентифицирует документ. Пара (URL, SeenAt) уникальна: одинаковый
SeenAt у одного URL означает повтор того же сообщения.
Требования
- Для каждого URL
Process возвращает склейку всех версий, увиденных к этому
моменту:
Body и SeenAt — из версии с максимальным SeenAt;
PublishedAt — из версии с минимальным SeenAt;
FirstSeenAt — минимальный SeenAt.
- Версии приходят в произвольном порядке, итог от порядка не зависит.
- Если новая версия ничего не поменяла в склейке (повтор сообщения или версия
«из середины»),
Process возвращает nil, nil — в очередь писать нечего.
- Поле
FirstSeenAt входного сообщения игнорируется.
- Пустой
URL → nil, ErrEmptyURL, состояние не меняется.
- Возвращённый
*Page принадлежит вызывающему: его изменение не влияет на
следующие результаты.
Пример
in := NewIngestor()
in.Process(Page{URL: "a", SeenAt: 20, PublishedAt: 2, Body: "v2"})
// → {a, PublishedAt: 2, SeenAt: 20, Body: "v2", FirstSeenAt: 20}
in.Process(Page{URL: "a", SeenAt: 10, PublishedAt: 1, Body: "v1"})
// → {a, PublishedAt: 1, SeenAt: 20, Body: "v2", FirstSeenAt: 10}
in.Process(Page{URL: "a", SeenAt: 15, PublishedAt: 9, Body: "v1.5"})
// → nil: ни минимум, ни максимум не изменились
in.Process(Page{URL: "a", SeenAt: 20, PublishedAt: 2, Body: "v2"})
// → nil: повтор
Устно: состояние растёт с числом URL — что делать, когда документов
миллиарды? Что будет после рестарта сервиса?