Senior
Dokumentu atjauninājumi nonāk pakalpojumā message Document { string Url = 1; // Dokumenta URL, tā unikālais identifikators uint64 PubDate = 2; // deklarētais publicēšanas laiks uint64 FetchTime = 3; // šī dokumenta atjauninājuma saņemšanas laiks, to var uzskatīt par versijas identifikatoru. Pāris (Url, FetchTime) ir unikāls. string Text = 4; // dokumenta teksts uint64 FirstFetchTime = 5; // sākotnēji nav, jāaizpilda } Dokumenti var ienākt jebkurā secībā (ne tajā, kā tie tika atjaunināti), un var būt dublēti ziņojumi. Izvadā ir nepieciešams veidot tādas pašas ziņas, bet ar labotiem laukiem saskaņā ar šīm noteikumiem (visu zemāk minēto attiecas uz dokumentu grupu ar vienu un to pašu Url lauku): Text un FetchTime lauki jābūt tādiem kā lielākajam līdz šim saņemtajam FetchTime dokumentam. PubDate jābūt tādam kā mazākajam FetchTime ziņojumam. FirstFetchTime jābūt vienādai ar mazāko FetchTime vērtību. Citādi sakot, katru brīdi mēs ņemam PubDate un FirstFetchTime no pirmās versijas, ko esam saņēmuši līdz šim (ja tās sakārtotu pēc FetchTime), un Text – no pēdējās. Koda interfeiss var tikt īstenots šādi: type Processor interface { Process(doc *Document) (*Document, error) } Šis kods darbosies pakalpojumā, kas lasa ziņas no rindas (Kafka vai līdzīgi), un arī raksta rezultātu atpakaļ rindā. Ja Process atgriež Null, nekas netiek rakstīts rindā.
sobes.tech AI
Atbilde no 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 и возвращать скорректированные документы согласно правилам.