mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-29 01:33:10 -06:00
Add required changes for JetStream
This commit is contained in:
parent
49311d83a3
commit
4fff7bf246
|
|
@ -256,11 +256,9 @@ func (t *EDUServerInputAPI) InputPresence(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m := &sarama.ProducerMessage{
|
_, err = t.JetStream.PublishMsg(&nats.Msg{
|
||||||
Topic: t.OutputPresenceTopic,
|
Subject: t.OutputPresenceTopic,
|
||||||
Key: sarama.StringEncoder(request.UserID),
|
Data: js,
|
||||||
Value: sarama.ByteEncoder(js),
|
})
|
||||||
}
|
|
||||||
_, _, err = t.Producer.SendMessage(m)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ package consumers
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/eduserver/api"
|
"github.com/matrix-org/dendrite/eduserver/api"
|
||||||
"github.com/matrix-org/dendrite/federationapi/queue"
|
"github.com/matrix-org/dendrite/federationapi/queue"
|
||||||
|
|
@ -40,6 +41,7 @@ type OutputEDUConsumer struct {
|
||||||
typingTopic string
|
typingTopic string
|
||||||
sendToDeviceTopic string
|
sendToDeviceTopic string
|
||||||
receiptTopic string
|
receiptTopic string
|
||||||
|
presenceTopic string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOutputEDUConsumer creates a new OutputEDUConsumer. Call Start() to begin consuming from EDU servers.
|
// NewOutputEDUConsumer creates a new OutputEDUConsumer. Call Start() to begin consuming from EDU servers.
|
||||||
|
|
@ -59,6 +61,7 @@ func NewOutputEDUConsumer(
|
||||||
typingTopic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputTypingEvent),
|
typingTopic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputTypingEvent),
|
||||||
sendToDeviceTopic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputSendToDeviceEvent),
|
sendToDeviceTopic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputSendToDeviceEvent),
|
||||||
receiptTopic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputReceiptEvent),
|
receiptTopic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputReceiptEvent),
|
||||||
|
presenceTopic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputPresenceData),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,8 +76,8 @@ func (t *OutputEDUConsumer) Start() error {
|
||||||
if _, err := t.jetstream.Subscribe(t.receiptTopic, t.onReceiptEvent); err != nil {
|
if _, err := t.jetstream.Subscribe(t.receiptTopic, t.onReceiptEvent); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := t.presenceConsumer.Start(); err != nil {
|
if _, err := t.jetstream.Subscribe(t.presenceTopic, t.onPresenceData); err != nil {
|
||||||
return fmt.Errorf("t.presenceConsumer.Start: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -256,52 +259,58 @@ func (t *OutputEDUConsumer) onReceiptEvent(msg *nats.Msg) {
|
||||||
|
|
||||||
// onPresenceData is called in response to a message received on the presence
|
// onPresenceData is called in response to a message received on the presence
|
||||||
// data topic from the EDU server.
|
// data topic from the EDU server.
|
||||||
func (t *OutputEDUConsumer) onPresenceData(msg *sarama.ConsumerMessage) error {
|
func (t *OutputEDUConsumer) onPresenceData(msg *nats.Msg) {
|
||||||
// Extract the presence data from msg.
|
jetstream.WithJetStreamMessage(msg, func(msg *nats.Msg) bool {
|
||||||
var presence api.OutputPresenceData
|
// Extract the presence data from msg.
|
||||||
if err := json.Unmarshal(msg.Value, &presence); err != nil {
|
var presence api.OutputPresenceData
|
||||||
// Skip this msg but continue processing messages.
|
if err := json.Unmarshal(msg.Data, &presence); err != nil {
|
||||||
log.WithError(err).Errorf("eduserver output log: message parse failed (expected presence)")
|
// Skip this msg but continue processing messages.
|
||||||
return nil
|
log.WithError(err).Errorf("eduserver output log: message parse failed (expected presence)")
|
||||||
}
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// only send presence events which originated from us
|
// only send presence events which originated from us
|
||||||
_, senderServerName, err := gomatrixserverlib.SplitID('@', presence.UserID)
|
_, senderServerName, err := gomatrixserverlib.SplitID('@', presence.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).WithField("user_id", presence.UserID).Error("Failed to extract domain from presence sender")
|
log.WithError(err).WithField("user_id", presence.UserID).Error("Failed to extract domain from presence sender")
|
||||||
return nil
|
return true
|
||||||
}
|
}
|
||||||
if senderServerName != t.ServerName {
|
if senderServerName != t.ServerName {
|
||||||
return nil // don't log, very spammy as it logs for each remote presence
|
return true // don't log, very spammy as it logs for each remote presence
|
||||||
}
|
}
|
||||||
|
|
||||||
joined, err := t.db.GetAllJoinedHosts(context.TODO())
|
joined, err := t.db.GetAllJoinedHosts(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.WithError(err).Error("failed to get all joined hosts")
|
||||||
}
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
lastActiveTS := time.Since(presence.LastActiveTS.Time())
|
lastActiveTS := time.Since(presence.LastActiveTS.Time())
|
||||||
content := api.FederationPresenceData{
|
content := api.FederationPresenceData{
|
||||||
Push: []api.FederationPresenceSingle{
|
Push: []api.FederationPresenceSingle{
|
||||||
{
|
{
|
||||||
CurrentlyActive: lastActiveTS < time.Minute*5,
|
CurrentlyActive: lastActiveTS < time.Minute*5,
|
||||||
LastActiveAgo: int(lastActiveTS.Milliseconds()),
|
LastActiveAgo: int(lastActiveTS.Milliseconds()),
|
||||||
Presence: presence.Presence.String(),
|
Presence: presence.Presence.String(),
|
||||||
UserID: presence.UserID,
|
UserID: presence.UserID,
|
||||||
StatusMsg: presence.StatusMsg,
|
StatusMsg: presence.StatusMsg,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
|
||||||
|
|
||||||
edu := &gomatrixserverlib.EDU{
|
edu := &gomatrixserverlib.EDU{
|
||||||
Type: gomatrixserverlib.MPresence,
|
Type: gomatrixserverlib.MPresence,
|
||||||
Origin: string(t.ServerName),
|
Origin: string(t.ServerName),
|
||||||
}
|
}
|
||||||
if edu.Content, err = json.Marshal(content); err != nil {
|
if edu.Content, err = json.Marshal(content); err != nil {
|
||||||
return err
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Sending edu to federated servers: %s", string(edu.Content))
|
log.Debugf("Sending edu to federated servers: %s", string(edu.Content))
|
||||||
|
if err := t.queues.SendEDU(edu, t.ServerName, joined); err != nil {
|
||||||
return t.queues.SendEDU(edu, t.ServerName, joined)
|
log.WithError(err).Error("failed to send EDU")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/setup/base"
|
"github.com/matrix-org/dendrite/setup/base"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
userAPI "github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/federationapi/routing"
|
"github.com/matrix-org/dendrite/federationapi/routing"
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ var (
|
||||||
OutputTypingEvent = "OutputTypingEvent"
|
OutputTypingEvent = "OutputTypingEvent"
|
||||||
OutputClientData = "OutputClientData"
|
OutputClientData = "OutputClientData"
|
||||||
OutputReceiptEvent = "OutputReceiptEvent"
|
OutputReceiptEvent = "OutputReceiptEvent"
|
||||||
|
OutputPresenceData = "OutputPresenceData"
|
||||||
)
|
)
|
||||||
|
|
||||||
var streams = []*nats.StreamConfig{
|
var streams = []*nats.StreamConfig{
|
||||||
|
|
@ -58,4 +59,9 @@ var streams = []*nats.StreamConfig{
|
||||||
Retention: nats.InterestPolicy,
|
Retention: nats.InterestPolicy,
|
||||||
Storage: nats.FileStorage,
|
Storage: nats.FileStorage,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: OutputPresenceData,
|
||||||
|
Retention: nats.InterestPolicy,
|
||||||
|
Storage: nats.FileStorage,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,26 +15,29 @@
|
||||||
package consumers
|
package consumers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/Shopify/sarama"
|
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
"github.com/matrix-org/dendrite/eduserver/api"
|
"github.com/matrix-org/dendrite/eduserver/api"
|
||||||
"github.com/matrix-org/dendrite/internal"
|
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||||
"github.com/matrix-org/dendrite/setup/process"
|
"github.com/matrix-org/dendrite/setup/process"
|
||||||
"github.com/matrix-org/dendrite/syncapi/notifier"
|
"github.com/matrix-org/dendrite/syncapi/notifier"
|
||||||
"github.com/matrix-org/dendrite/syncapi/storage"
|
"github.com/matrix-org/dendrite/syncapi/storage"
|
||||||
"github.com/matrix-org/dendrite/syncapi/types"
|
"github.com/matrix-org/dendrite/syncapi/types"
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OutputPresenceDataConsumer consumes events that originated in the EDU server.
|
// OutputPresenceDataConsumer consumes events that originated in the EDU server.
|
||||||
type OutputPresenceDataConsumer struct {
|
type OutputPresenceDataConsumer struct {
|
||||||
presenceConsumer *internal.ContinualConsumer
|
ctx context.Context
|
||||||
db storage.Database
|
jetstream nats.JetStreamContext
|
||||||
stream types.StreamProvider
|
topic string
|
||||||
notifier *notifier.Notifier
|
db storage.Database
|
||||||
|
stream types.StreamProvider
|
||||||
|
notifier *notifier.Notifier
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOutputPresenceDataConsumer creates a new OutputPresenceDataConsumer.
|
// NewOutputPresenceDataConsumer creates a new OutputPresenceDataConsumer.
|
||||||
|
|
@ -42,49 +45,41 @@ type OutputPresenceDataConsumer struct {
|
||||||
func NewOutputPresenceDataConsumer(
|
func NewOutputPresenceDataConsumer(
|
||||||
process *process.ProcessContext,
|
process *process.ProcessContext,
|
||||||
cfg *config.SyncAPI,
|
cfg *config.SyncAPI,
|
||||||
kafkaConsumer sarama.Consumer,
|
js nats.JetStreamContext,
|
||||||
store storage.Database,
|
store storage.Database,
|
||||||
notifier *notifier.Notifier,
|
notifier *notifier.Notifier,
|
||||||
stream types.StreamProvider,
|
stream types.StreamProvider,
|
||||||
) *OutputPresenceDataConsumer {
|
) *OutputPresenceDataConsumer {
|
||||||
|
return &OutputPresenceDataConsumer{
|
||||||
consumer := internal.ContinualConsumer{
|
ctx: process.Context(),
|
||||||
Process: process,
|
jetstream: js,
|
||||||
ComponentName: "syncapi/eduserver/presence",
|
topic: cfg.Matrix.JetStream.TopicFor(jetstream.OutputPresenceData),
|
||||||
Topic: cfg.Matrix.Kafka.TopicFor(config.TopicOutputPresenceData),
|
db: store,
|
||||||
Consumer: kafkaConsumer,
|
notifier: notifier,
|
||||||
PartitionStore: store,
|
stream: stream,
|
||||||
}
|
}
|
||||||
|
|
||||||
s := &OutputPresenceDataConsumer{
|
|
||||||
presenceConsumer: &consumer,
|
|
||||||
db: store,
|
|
||||||
notifier: notifier,
|
|
||||||
stream: stream,
|
|
||||||
}
|
|
||||||
|
|
||||||
consumer.ProcessMessage = s.onMessage
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start consuming from EDU api
|
// Start consuming from EDU api
|
||||||
func (s *OutputPresenceDataConsumer) Start() error {
|
func (s *OutputPresenceDataConsumer) Start() error {
|
||||||
return s.presenceConsumer.Start()
|
_, err := s.jetstream.Subscribe(s.topic, s.onMessage)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OutputPresenceDataConsumer) onMessage(msg *sarama.ConsumerMessage) error {
|
func (s *OutputPresenceDataConsumer) onMessage(msg *nats.Msg) {
|
||||||
var output api.OutputPresenceData
|
jetstream.WithJetStreamMessage(msg, func(msg *nats.Msg) bool {
|
||||||
if err := json.Unmarshal(msg.Value, &output); err != nil {
|
var output api.OutputPresenceData
|
||||||
// If the message was invalid, log it and move on to the next message in the stream
|
if err := json.Unmarshal(msg.Data, &output); err != nil {
|
||||||
log.WithError(err).Errorf("EDU server output log: message parse failure")
|
// If the message was invalid, log it and move on to the next message in the stream
|
||||||
sentry.CaptureException(err)
|
log.WithError(err).Errorf("EDU server output log: message parse failure")
|
||||||
return nil
|
sentry.CaptureException(err)
|
||||||
}
|
return true
|
||||||
log.Debugf("presence received by sync api! %+v", output)
|
}
|
||||||
|
log.Debugf("presence received by sync api! %+v", output)
|
||||||
|
|
||||||
s.stream.Advance(output.StreamPos)
|
s.stream.Advance(output.StreamPos)
|
||||||
s.notifier.OnNewPresence(types.StreamingToken{PresenceDataPosition: output.StreamPos}, output.UserID)
|
s.notifier.OnNewPresence(types.StreamingToken{PresenceDataPosition: output.StreamPos}, output.UserID)
|
||||||
|
return true
|
||||||
return nil
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ func AddPublicRoutes(
|
||||||
}
|
}
|
||||||
|
|
||||||
presenceConsumer := consumers.NewOutputPresenceDataConsumer(
|
presenceConsumer := consumers.NewOutputPresenceDataConsumer(
|
||||||
process, cfg, consumer, syncDB, notifier, streams.PresenceDataStreamProvider,
|
process, cfg, js, syncDB, notifier, streams.PresenceDataStreamProvider,
|
||||||
)
|
)
|
||||||
if err = presenceConsumer.Start(); err != nil {
|
if err = presenceConsumer.Start(); err != nil {
|
||||||
logrus.WithError(err).Panicf("failed to start presence consumer")
|
logrus.WithError(err).Panicf("failed to start presence consumer")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue