Sobes.tech
Senior

Aktualizácie dokumentov prichádzajú do služby message Document { string Url = 1; // URL dokumentu, jeho jedinečný identifikátor uint64 PubDate = 2; // deklarovaný čas publikácie dokumentu uint64 FetchTime = 3; // čas prijatia tejto aktualizácie dokumentu, môže byť považovaný za identifikátor verzie. Pár (Url, FetchTime) je jedinečný. string Text = 4; // text dokumentu uint64 FirstFetchTime = 5; // zpočiatku chýba, treba doplniť } Dokumenty môžu prísť v ľubovoľnom poradí (nie v poradí, v ktorom boli aktualizované), a môžu byť aj duplicitné správy. Je potrebné na výstupe vytvoriť rovnaké správy, ale s opravenými poľami podľa nasledujúcich pravidiel (všetko nižšie platí pre skupinu dokumentov so rovnakým poľom Url): Pole Text a FetchTime by mali byť také, aké boli v dokumente s najväčším FetchTime, ktorý bol doteraz prijatý. Pole PubDate by malo byť také, aké bolo v správe s najmenším FetchTime. Pole FirstFetchTime by malo byť rovné najnižšej hodnoty FetchTime. Inými slovami, v každom momente berieme PubDate a FirstFetchTime z prvej verzie, ktorú sme doteraz obdržali (ak ich zoradíme podľa FetchTime), a Text z poslednej. Rozhranie v kóde môže byť implementované takto: type Processor interface { Process(doc *Document) (*Document, error) } Tento kód bude fungovať v službe, ktorá číta správy z fronty (Kafka alebo podobne), a tiež zapisuje výsledok do fronty. Ak Process vráti Null, nič sa do fronty nezapisuje.

sobes.tech AI

Odpoveď 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 и возвращать скорректированные документы согласно правилам.