Sobes.tech
Senior

Aktualizace dokumentů přicházejí do služby message Document { string Url = 1; // URL dokumentu, jeho jedinečný identifikátor uint64 PubDate = 2; // deklarovaný čas publikace dokumentu uint64 FetchTime = 3; // čas přijetí této aktualizace dokumentu, může být považován za identifikátor verze. Pár (Url, FetchTime) je jedinečný. string Text = 4; // text dokumentu uint64 FirstFetchTime = 5; // zpočátku chybí, je třeba doplnit } Dokumenty mohou přijít v libovolném pořadí (ne v pořadí, ve kterém byly aktualizovány), a mohou být i duplicitní zprávy. Je třeba na výstupu vytvořit stejné zprávy, ale s opravenými poli podle následujících pravidel (všechno níže platí pro skupinu dokumentů se stejným polem Url): Pole Text a FetchTime by měla být taková, jaká byla u dokumentu s největším FetchTime, který byl dosud přijat. Pole PubDate by mělo být takové, jaké bylo u zprávy s nejmenším FetchTime. Pole FirstFetchTime by mělo být rovno nejnižší hodnotě FetchTime. Jinými slovy, v každém okamžiku bereme PubDate a FirstFetchTime z první verze, kterou jsme dosud obdrželi (pokud je seřadíme podle FetchTime), a Text z poslední. Rozhraní v kódu lze implementovat takto: type Processor interface { Process(doc *Document) (*Document, error) } Tento kód bude fungovat v službě, která čte zprávy z fronty (Kafka nebo podobné), a také zapisuje výsledek do fronty. Pokud Process vrací Null, nic se do fronty nezapisuje.

sobes.tech AI

Odpověď od AI

Для реализации интерфейса Processor в Go нужно хранить состояние по каждому Url, чтобы на каждый входящий документ обновлять агрегированное состояние и возвращать скорректированное сообщение.

Основная идея:

  • Для каждого Url хранить:
    • минимальный FetchTime (для PubDate и FirstFetchTime)
    • максимальный FetchTime (для Text и FetchTime)
    • PubDate, Text, FirstFetchTime, соответствующие этим минимальному и максимальному FetchTime

При получении нового документа:

  • Если для Url нет записи — создать с данными из документа
  • Иначе обновить минимальный и максимальный FetchTime и соответствующие поля при необходимости
  • Вернуть агрегированный документ с нужными полями

Пример реализации:

import (
    "errors"
    "sync"
)

type Document struct {
    Url            string
    PubDate        uint64
    FetchTime      uint64
    Text           string
    FirstFetchTime uint64
}

type Processor interface {
    Process(doc *Document) (*Document, error)
}

type docState struct {
    minFetchTime uint64
    maxFetchTime uint64
    pubDate      uint64
    text         string
    firstFetch   uint64
}

type processorImpl struct {
    mu    sync.Mutex
    state map[string]*docState
}

func NewProcessor() Processor {
    return &processorImpl{
        state: make(map[string]*docState),
    }
}

func (p *processorImpl) Process(doc *Document) (*Document, error) {
    if doc == nil {
        return nil, errors.New("input document is nil")
    }

    p.mu.Lock()
    defer p.mu.Unlock()

    st, ok := p.state[doc.Url]
    if !ok {
        // первый документ для данного Url
        p.state[doc.Url] = &docState{
            minFetchTime: doc.FetchTime,
            maxFetchTime: doc.FetchTime,
            pubDate:      doc.PubDate,
            text:         doc.Text,
            firstFetch:   doc.FetchTime,
        }
        return &Document{
            Url:            doc.Url,
            PubDate:        doc.PubDate,
            FetchTime:      doc.FetchTime,
            Text:           doc.Text,
            FirstFetchTime: doc.FetchTime,
        }, nil
    }

    updated := false

    // Обновляем минимальный FetchTime и PubDate, FirstFetchTime
    if doc.FetchTime < st.minFetchTime {
        st.minFetchTime = doc.FetchTime
        st.pubDate = doc.PubDate
        st.firstFetch = doc.FetchTime
        updated = true
    }

    // Обновляем максимальный FetchTime и Text
    if doc.FetchTime > st.maxFetchTime {
        st.maxFetchTime = doc.FetchTime
        st.text = doc.Text
        updated = true
    }

    // Если пришло сообщение с FetchTime, который уже есть, но текст или PubDate отличаются,
    // можно игнорировать, так как пара (Url, FetchTime) уникальна, либо обновлять по логике.

    if !updated {
        // Данные не изменились, можно вернуть nil, чтобы не писать в очередь
        return nil, nil
    }

    return &Document{
        Url:            doc.Url,
        PubDate:        st.pubDate,
        FetchTime:      st.maxFetchTime,
        Text:           st.text,
        FirstFetchTime: st.firstFetch,
    }, nil
}

Таким образом, сервис будет поддерживать актуальное агрегированное состояние для каждого Url и возвращать скорректированные документы согласно правилам.