Merge branch 'main' of github.com:matrix-org/dendrite into s7evink/hisvismessages

This commit is contained in:
Till Faelligen 2022-07-20 13:18:50 +02:00
commit 6d7fd13c82
No known key found for this signature in database
GPG key ID: 3DF82D8AB9211D4E
10 changed files with 115 additions and 48 deletions

View file

@ -86,9 +86,12 @@ would be a huge help too, as that will help us to understand where the memory us
You may need to revisit the connection limit of your PostgreSQL server and/or make changes to the `max_connections` lines in your Dendrite configuration. Be aware that each Dendrite component opens its own database connections and has its own connection limit, even in monolith mode! You may need to revisit the connection limit of your PostgreSQL server and/or make changes to the `max_connections` lines in your Dendrite configuration. Be aware that each Dendrite component opens its own database connections and has its own connection limit, even in monolith mode!
## What is being reported when enabling anonymous stats? ## What is being reported when enabling phone-home statistics?
If anonymous stats reporting is enabled, the following data is send to the defined endpoint. Phone-home statistics contain your server's domain name, some configuration information about
your deployment and aggregated information about active users on your deployment. They are sent
to the endpoint URL configured in your Dendrite configuration file only. The following is an
example of the data that is sent:
```json ```json
{ {
@ -106,7 +109,7 @@ If anonymous stats reporting is enabled, the following data is send to the defin
"go_arch": "amd64", "go_arch": "amd64",
"go_os": "linux", "go_os": "linux",
"go_version": "go1.16.13", "go_version": "go1.16.13",
"homeserver": "localhost:8800", "homeserver": "my.domain.com",
"log_level": "trace", "log_level": "trace",
"memory_rss": 93452, "memory_rss": 93452,
"monolith": true, "monolith": true,

View file

@ -14,15 +14,18 @@ that take the format `@user:example.com`.
For federation to work, the server name must be resolvable by other homeservers on the internet For federation to work, the server name must be resolvable by other homeservers on the internet
— that is, the domain must be registered and properly configured with the relevant DNS records. — that is, the domain must be registered and properly configured with the relevant DNS records.
Matrix servers discover each other when federating using the following methods: Matrix servers usually discover each other when federating using the following methods:
1. If a well-known delegation exists on `example.com`, use the path server from the 1. If a well-known delegation exists on `example.com`, use the domain and port from the
well-known file to connect to the remote homeserver; well-known file to connect to the remote homeserver;
2. If a DNS SRV delegation exists on `example.com`, use the hostname and port from the DNS SRV 2. If a DNS SRV delegation exists on `example.com`, use the IP address and port from the DNS SRV
record to connect to the remote homeserver; record to connect to the remote homeserver;
3. If neither well-known or DNS SRV delegation are configured, attempt to connect to the remote 3. If neither well-known or DNS SRV delegation are configured, attempt to connect to the remote
homeserver by connecting to `example.com` port TCP/8448 using HTTPS. homeserver by connecting to `example.com` port TCP/8448 using HTTPS.
The exact details of how server name resolution works can be found in
[the spec](https://spec.matrix.org/v1.3/server-server-api/#resolving-server-names).
## TLS certificates ## TLS certificates
Matrix federation requires that valid TLS certificates are present on the domain. You must Matrix federation requires that valid TLS certificates are present on the domain. You must
@ -51,17 +54,12 @@ you will be able to delegate from `example.com` to `matrix.example.com` so that
Delegation can be performed in one of two ways: Delegation can be performed in one of two ways:
* **Well-known delegation**: A well-known text file is served over HTTPS on the domain name * **Well-known delegation (preferred)**: A well-known text file is served over HTTPS on the domain
that you want to use, pointing to your server on `matrix.example.com` port 8448; name that you want to use, pointing to your server on `matrix.example.com` port 8448;
* **DNS SRV delegation**: A DNS SRV record is created on the domain name that you want to * **DNS SRV delegation (not recommended)**: See the SRV delegation section below for details.
use, pointing to your server on `matrix.example.com` port TCP/8448.
If you are using a reverse proxy to forward `/_matrix` to Dendrite, your well-known or DNS SRV If you are using a reverse proxy to forward `/_matrix` to Dendrite, your well-known or delegation
delegation must refer to the hostname and port that the reverse proxy is listening on instead. must refer to the hostname and port that the reverse proxy is listening on instead.
Well-known delegation is typically easier to set up and usually preferred. However, you can use
either or both methods to delegate. If you configure both methods of delegation, it is important
that they both agree and refer to the same hostname and port.
## Well-known delegation ## Well-known delegation
@ -74,20 +72,36 @@ and contain the following JSON document:
```json ```json
{ {
"m.server": "https://matrix.example.com:8448" "m.server": "matrix.example.com:8448"
} }
``` ```
You can also serve `.well-known` with Dendrite itself by setting the `well_known_server_name` config
option to the value you want for `m.server`. This is primarily useful if Dendrite is exposed on
`example.com:443` and you don't want to set up a separate webserver just for serving the `.well-known`
file.
```yaml
global:
...
well_known_server_name: "example.com:443"
```
## DNS SRV delegation ## DNS SRV delegation
Using DNS SRV delegation requires creating DNS SRV records on the `example.com` zone which This method is not recommended, as the behavior of SRV records in Matrix is rather unintuitive:
refer to your Dendrite installation. SRV records will only change the IP address and port that other servers connect to, they won't
affect the domain name. In technical terms, the `Host` header and TLS SNI of federation requests
will still be `example.com` even if the SRV record points at `matrix.example.com`.
Assuming that your Dendrite installation is listening for HTTPS connections at `matrix.example.com` In practice, this means that the server must be configured with valid TLS certificates for
port 8448, the DNS SRV record must have the following fields: `example.com`, rather than `matrix.example.com` as one might intuitively expect. If there's a
reverse proxy in between, the proxy configuration must be written as if it's `example.com`, as the
proxy will never see the name `matrix.example.com` in incoming requests.
* Name: `@` (or whichever term your DNS provider uses to signal the root) This behavior also means that if `example.com` and `matrix.example.com` point at the same IP
* Service: `_matrix` address, there is no reason to have a SRV record pointing at `matrix.example.com`. It can still
* Protocol: `_tcp` be used to change the port number, but it won't do anything else.
* Port: `8448`
* Target: `matrix.example.com` If you understand how SRV records work and still want to use them, the service name is `_matrix` and
the protocol is `_tcp`.

View file

@ -0,0 +1,18 @@
package caching
import "github.com/matrix-org/dendrite/roomserver/types"
// EventStateKeyCache contains the subset of functions needed for
// a room event state key cache.
type EventStateKeyCache interface {
GetEventStateKey(eventStateKeyNID types.EventStateKeyNID) (string, bool)
StoreEventStateKey(eventStateKeyNID types.EventStateKeyNID, eventStateKey string)
}
func (c Caches) GetEventStateKey(eventStateKeyNID types.EventStateKeyNID) (string, bool) {
return c.RoomServerStateKeys.Get(eventStateKeyNID)
}
func (c Caches) StoreEventStateKey(eventStateKeyNID types.EventStateKeyNID, eventStateKey string) {
c.RoomServerStateKeys.Set(eventStateKeyNID, eventStateKey)
}

View file

@ -9,6 +9,7 @@ type RoomServerCaches interface {
RoomVersionCache RoomVersionCache
RoomInfoCache RoomInfoCache
RoomServerEventsCache RoomServerEventsCache
EventStateKeyCache
} }
// RoomServerNIDsCache contains the subset of functions needed for // RoomServerNIDsCache contains the subset of functions needed for
@ -19,9 +20,9 @@ type RoomServerNIDsCache interface {
} }
func (c Caches) GetRoomServerRoomID(roomNID types.RoomNID) (string, bool) { func (c Caches) GetRoomServerRoomID(roomNID types.RoomNID) (string, bool) {
return c.RoomServerRoomIDs.Get(int64(roomNID)) return c.RoomServerRoomIDs.Get(roomNID)
} }
func (c Caches) StoreRoomServerRoomID(roomNID types.RoomNID, roomID string) { func (c Caches) StoreRoomServerRoomID(roomNID types.RoomNID, roomID string) {
c.RoomServerRoomIDs.Set(int64(roomNID), roomID) c.RoomServerRoomIDs.Set(roomNID, roomID)
} }

View file

@ -23,16 +23,17 @@ import (
// different implementations as long as they satisfy the Cache // different implementations as long as they satisfy the Cache
// interface. // interface.
type Caches struct { type Caches struct {
RoomVersions Cache[string, gomatrixserverlib.RoomVersion] // room ID -> room version RoomVersions Cache[string, gomatrixserverlib.RoomVersion] // room ID -> room version
ServerKeys Cache[string, gomatrixserverlib.PublicKeyLookupResult] // server name -> server keys ServerKeys Cache[string, gomatrixserverlib.PublicKeyLookupResult] // server name -> server keys
RoomServerRoomNIDs Cache[string, types.RoomNID] // room ID -> room NID RoomServerRoomNIDs Cache[string, types.RoomNID] // room ID -> room NID
RoomServerRoomIDs Cache[int64, string] // room NID -> room ID RoomServerRoomIDs Cache[types.RoomNID, string] // room NID -> room ID
RoomServerEvents Cache[int64, *gomatrixserverlib.Event] // event NID -> event RoomServerEvents Cache[int64, *gomatrixserverlib.Event] // event NID -> event
RoomInfos Cache[string, *types.RoomInfo] // room ID -> room info RoomServerStateKeys Cache[types.EventStateKeyNID, string] // event NID -> event state key
FederationPDUs Cache[int64, *gomatrixserverlib.HeaderedEvent] // queue NID -> PDU RoomInfos Cache[string, *types.RoomInfo] // room ID -> room info
FederationEDUs Cache[int64, *gomatrixserverlib.EDU] // queue NID -> EDU FederationPDUs Cache[int64, *gomatrixserverlib.HeaderedEvent] // queue NID -> PDU
SpaceSummaryRooms Cache[string, gomatrixserverlib.MSC2946SpacesResponse] // room ID -> space response FederationEDUs Cache[int64, *gomatrixserverlib.EDU] // queue NID -> EDU
LazyLoading Cache[lazyLoadingCacheKey, string] // composite key -> event ID SpaceSummaryRooms Cache[string, gomatrixserverlib.MSC2946SpacesResponse] // room ID -> space response
LazyLoading Cache[lazyLoadingCacheKey, string] // composite key -> event ID
} }
// Cache is the interface that an implementation must satisfy. // Cache is the interface that an implementation must satisfy.
@ -44,7 +45,7 @@ type Cache[K keyable, T any] interface {
type keyable interface { type keyable interface {
// from https://github.com/dgraph-io/ristretto/blob/8e850b710d6df0383c375ec6a7beae4ce48fc8d5/z/z.go#L34 // from https://github.com/dgraph-io/ristretto/blob/8e850b710d6df0383c375ec6a7beae4ce48fc8d5/z/z.go#L34
uint64 | string | []byte | byte | int | int32 | uint32 | int64 | lazyLoadingCacheKey ~uint64 | ~string | []byte | byte | ~int | ~int32 | ~uint32 | ~int64 | lazyLoadingCacheKey
} }
type costable interface { type costable interface {

View file

@ -40,6 +40,7 @@ const (
federationEDUsCache federationEDUsCache
spaceSummaryRoomsCache spaceSummaryRoomsCache
lazyLoadingCache lazyLoadingCache
eventStateKeyCache
) )
func NewRistrettoCache(maxCost config.DataUnit, maxAge time.Duration, enablePrometheus bool) *Caches { func NewRistrettoCache(maxCost config.DataUnit, maxAge time.Duration, enablePrometheus bool) *Caches {
@ -88,7 +89,7 @@ func NewRistrettoCache(maxCost config.DataUnit, maxAge time.Duration, enableProm
Prefix: roomNIDsCache, Prefix: roomNIDsCache,
MaxAge: maxAge, MaxAge: maxAge,
}, },
RoomServerRoomIDs: &RistrettoCachePartition[int64, string]{ // room NID -> room ID RoomServerRoomIDs: &RistrettoCachePartition[types.RoomNID, string]{ // room NID -> room ID
cache: cache, cache: cache,
Prefix: roomIDsCache, Prefix: roomIDsCache,
MaxAge: maxAge, MaxAge: maxAge,
@ -100,6 +101,11 @@ func NewRistrettoCache(maxCost config.DataUnit, maxAge time.Duration, enableProm
MaxAge: maxAge, MaxAge: maxAge,
}, },
}, },
RoomServerStateKeys: &RistrettoCachePartition[types.EventStateKeyNID, string]{ // event NID -> event state key
cache: cache,
Prefix: eventStateKeyCache,
MaxAge: maxAge,
},
RoomInfos: &RistrettoCachePartition[string, *types.RoomInfo]{ // room ID -> room info RoomInfos: &RistrettoCachePartition[string, *types.RoomInfo]{ // room ID -> room info
cache: cache, cache: cache,
Prefix: roomInfosCache, Prefix: roomInfosCache,

View file

@ -105,8 +105,15 @@ func (u *MembershipUpdater) SetToInvite(event *gomatrixserverlib.Event) (bool, e
if err != nil { if err != nil {
return fmt.Errorf("u.d.InvitesTable.InsertInviteEvent: %w", err) return fmt.Errorf("u.d.InvitesTable.InsertInviteEvent: %w", err)
} }
// Look up the NID of the invite event
nIDs, err := u.d.eventNIDs(u.ctx, u.txn, []string{event.EventID()}, false)
if err != nil {
return fmt.Errorf("u.d.EventNIDs: %w", err)
}
if u.membership != tables.MembershipStateInvite { if u.membership != tables.MembershipStateInvite {
if inserted, err = u.d.MembershipTable.UpdateMembership(u.ctx, u.txn, u.roomNID, u.targetUserNID, senderUserNID, tables.MembershipStateInvite, 0, false); err != nil { if inserted, err = u.d.MembershipTable.UpdateMembership(u.ctx, u.txn, u.roomNID, u.targetUserNID, senderUserNID, tables.MembershipStateInvite, nIDs[event.EventID()], false); err != nil {
return fmt.Errorf("u.d.MembershipTable.UpdateMembership: %w", err) return fmt.Errorf("u.d.MembershipTable.UpdateMembership: %w", err)
} }
} }

View file

@ -72,7 +72,24 @@ func (d *Database) eventTypeNIDs(
func (d *Database) EventStateKeys( func (d *Database) EventStateKeys(
ctx context.Context, eventStateKeyNIDs []types.EventStateKeyNID, ctx context.Context, eventStateKeyNIDs []types.EventStateKeyNID,
) (map[types.EventStateKeyNID]string, error) { ) (map[types.EventStateKeyNID]string, error) {
return d.EventStateKeysTable.BulkSelectEventStateKey(ctx, nil, eventStateKeyNIDs) result := make(map[types.EventStateKeyNID]string, len(eventStateKeyNIDs))
fetch := make([]types.EventStateKeyNID, 0, len(eventStateKeyNIDs))
for _, nid := range eventStateKeyNIDs {
if key, ok := d.Cache.GetEventStateKey(nid); ok {
result[nid] = key
} else {
fetch = append(fetch, nid)
}
}
fromDB, err := d.EventStateKeysTable.BulkSelectEventStateKey(ctx, nil, fetch)
if err != nil {
return nil, err
}
for nid, key := range fromDB {
result[nid] = key
d.Cache.StoreEventStateKey(nid, key)
}
return result, nil
} }
func (d *Database) EventStateKeyNIDs( func (d *Database) EventStateKeyNIDs(

View file

@ -73,7 +73,7 @@ type Global struct {
// ServerNotices configuration used for sending server notices // ServerNotices configuration used for sending server notices
ServerNotices ServerNotices `yaml:"server_notices"` ServerNotices ServerNotices `yaml:"server_notices"`
// ReportStats configures opt-in anonymous stats reporting. // ReportStats configures opt-in phone-home statistics reporting.
ReportStats ReportStats `yaml:"report_stats"` ReportStats ReportStats `yaml:"report_stats"`
// Configuration for the caches. // Configuration for the caches.
@ -189,9 +189,9 @@ func (c *Cache) Verify(errors *ConfigErrors, isMonolith bool) {
checkPositive(errors, "max_size_estimated", int64(c.EstimatedMaxSize)) checkPositive(errors, "max_size_estimated", int64(c.EstimatedMaxSize))
} }
// ReportStats configures opt-in anonymous stats reporting. // ReportStats configures opt-in phone-home statistics reporting.
type ReportStats struct { type ReportStats struct {
// Enabled configures anonymous usage stats of the server // Enabled configures phone-home statistics of the server
Enabled bool `yaml:"enabled"` Enabled bool `yaml:"enabled"`
// Endpoint the endpoint to report stats to // Endpoint the endpoint to report stats to

View file

@ -139,7 +139,7 @@ func (p *phoneHomeStats) collect() {
output := bytes.Buffer{} output := bytes.Buffer{}
if err = json.NewEncoder(&output).Encode(p.stats); err != nil { if err = json.NewEncoder(&output).Encode(p.stats); err != nil {
logrus.WithError(err).Error("unable to encode anonymous stats") logrus.WithError(err).Error("Unable to encode phone-home statistics")
return return
} }
@ -147,14 +147,14 @@ func (p *phoneHomeStats) collect() {
request, err := http.NewRequestWithContext(ctx, http.MethodPost, p.cfg.Global.ReportStats.Endpoint, &output) request, err := http.NewRequestWithContext(ctx, http.MethodPost, p.cfg.Global.ReportStats.Endpoint, &output)
if err != nil { if err != nil {
logrus.WithError(err).Error("unable to create anonymous stats request") logrus.WithError(err).Error("Unable to create phone-home statistics request")
return return
} }
request.Header.Set("User-Agent", "Dendrite/"+internal.VersionString()) request.Header.Set("User-Agent", "Dendrite/"+internal.VersionString())
_, err = p.client.Do(request) _, err = p.client.Do(request)
if err != nil { if err != nil {
logrus.WithError(err).Error("unable to send anonymous stats") logrus.WithError(err).Error("Unable to send phone-home statistics")
return return
} }
} }