Merge branch 'matrix-org:main' into presence-optimization

This commit is contained in:
jjj333_p 2024-03-04 16:25:41 -10:00 committed by GitHub
commit b436d43133
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 180 additions and 78 deletions

3
.gitignore vendored
View file

@ -78,3 +78,6 @@ build
# golang workspaces # golang workspaces
go.work* go.work*
# helm chart
helm/dendrite/charts/

View file

@ -117,6 +117,7 @@ The list of files that need to be stored is:
- matrix-key.pem - matrix-key.pem
- dendrite.yaml - dendrite.yaml
- the postgres or sqlite DB - the postgres or sqlite DB
- the jetstream directory
- the media store - the media store
- the search index (although this can be regenerated) - the search index (although this can be regenerated)

View file

@ -231,9 +231,9 @@ GEM
jekyll-seo-tag (~> 2.1) jekyll-seo-tag (~> 2.1)
minitest (5.17.0) minitest (5.17.0)
multipart-post (2.1.1) multipart-post (2.1.1)
nokogiri (1.14.3-arm64-darwin) nokogiri (1.16.2-arm64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.14.3-x86_64-linux) nokogiri (1.16.2-x86_64-linux)
racc (~> 1.4) racc (~> 1.4)
octokit (4.22.0) octokit (4.22.0)
faraday (>= 0.9) faraday (>= 0.9)
@ -241,7 +241,7 @@ GEM
pathutil (0.16.2) pathutil (0.16.2)
forwardable-extended (~> 2.6) forwardable-extended (~> 2.6)
public_suffix (4.0.7) public_suffix (4.0.7)
racc (1.6.2) racc (1.7.3)
rb-fsevent (0.11.1) rb-fsevent (0.11.1)
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)

View file

@ -113,10 +113,7 @@ func NewInternalAPI(
_ = federationDB.RemoveAllServersFromBlacklist() _ = federationDB.RemoveAllServersFromBlacklist()
} }
stats := statistics.NewStatistics( stats := statistics.NewStatistics(federationDB, cfg.FederationMaxRetries+1, cfg.P2PFederationRetriesUntilAssumedOffline+1, cfg.EnableRelays)
federationDB,
cfg.FederationMaxRetries+1,
cfg.P2PFederationRetriesUntilAssumedOffline+1)
js, nats := natsInstance.Prepare(processContext, &cfg.Matrix.JetStream) js, nats := natsInstance.Prepare(processContext, &cfg.Matrix.JetStream)

View file

@ -61,7 +61,7 @@ func TestFederationClientQueryKeys(t *testing.T) {
}, },
} }
fedClient := &testFedClient{} fedClient := &testFedClient{}
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline) stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
queues := queue.NewOutgoingQueues( queues := queue.NewOutgoingQueues(
testDB, process.NewProcessContext(), testDB, process.NewProcessContext(),
false, false,
@ -92,7 +92,7 @@ func TestFederationClientQueryKeysBlacklisted(t *testing.T) {
}, },
} }
fedClient := &testFedClient{} fedClient := &testFedClient{}
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline) stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
queues := queue.NewOutgoingQueues( queues := queue.NewOutgoingQueues(
testDB, process.NewProcessContext(), testDB, process.NewProcessContext(),
false, false,
@ -122,7 +122,7 @@ func TestFederationClientQueryKeysFailure(t *testing.T) {
}, },
} }
fedClient := &testFedClient{shouldFail: true} fedClient := &testFedClient{shouldFail: true}
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline) stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
queues := queue.NewOutgoingQueues( queues := queue.NewOutgoingQueues(
testDB, process.NewProcessContext(), testDB, process.NewProcessContext(),
false, false,
@ -152,7 +152,7 @@ func TestFederationClientClaimKeys(t *testing.T) {
}, },
} }
fedClient := &testFedClient{} fedClient := &testFedClient{}
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline) stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
queues := queue.NewOutgoingQueues( queues := queue.NewOutgoingQueues(
testDB, process.NewProcessContext(), testDB, process.NewProcessContext(),
false, false,
@ -183,7 +183,7 @@ func TestFederationClientClaimKeysBlacklisted(t *testing.T) {
}, },
} }
fedClient := &testFedClient{} fedClient := &testFedClient{}
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline) stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
queues := queue.NewOutgoingQueues( queues := queue.NewOutgoingQueues(
testDB, process.NewProcessContext(), testDB, process.NewProcessContext(),
false, false,

View file

@ -66,7 +66,7 @@ func TestPerformWakeupServers(t *testing.T) {
}, },
} }
fedClient := &testFedClient{} fedClient := &testFedClient{}
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline) stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, true)
queues := queue.NewOutgoingQueues( queues := queue.NewOutgoingQueues(
testDB, process.NewProcessContext(), testDB, process.NewProcessContext(),
false, false,
@ -112,7 +112,7 @@ func TestQueryRelayServers(t *testing.T) {
}, },
} }
fedClient := &testFedClient{} fedClient := &testFedClient{}
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline) stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
queues := queue.NewOutgoingQueues( queues := queue.NewOutgoingQueues(
testDB, process.NewProcessContext(), testDB, process.NewProcessContext(),
false, false,
@ -153,7 +153,7 @@ func TestRemoveRelayServers(t *testing.T) {
}, },
} }
fedClient := &testFedClient{} fedClient := &testFedClient{}
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline) stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
queues := queue.NewOutgoingQueues( queues := queue.NewOutgoingQueues(
testDB, process.NewProcessContext(), testDB, process.NewProcessContext(),
false, false,
@ -193,7 +193,7 @@ func TestPerformDirectoryLookup(t *testing.T) {
}, },
} }
fedClient := &testFedClient{} fedClient := &testFedClient{}
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline) stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
queues := queue.NewOutgoingQueues( queues := queue.NewOutgoingQueues(
testDB, process.NewProcessContext(), testDB, process.NewProcessContext(),
false, false,
@ -232,7 +232,7 @@ func TestPerformDirectoryLookupRelaying(t *testing.T) {
}, },
} }
fedClient := &testFedClient{} fedClient := &testFedClient{}
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline) stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, true)
queues := queue.NewOutgoingQueues( queues := queue.NewOutgoingQueues(
testDB, process.NewProcessContext(), testDB, process.NewProcessContext(),
false, false,

View file

@ -117,7 +117,7 @@ func testSetup(failuresUntilBlacklist uint32, failuresUntilAssumedOffline uint32
txRelayCount: *atomic.NewUint32(0), txRelayCount: *atomic.NewUint32(0),
} }
stats := statistics.NewStatistics(db, failuresUntilBlacklist, failuresUntilAssumedOffline) stats := statistics.NewStatistics(db, failuresUntilBlacklist, failuresUntilAssumedOffline, false)
signingInfo := []*fclient.SigningIdentity{ signingInfo := []*fclient.SigningIdentity{
{ {
KeyID: "ed21019:auto", KeyID: "ed21019:auto",

View file

@ -34,12 +34,15 @@ type Statistics struct {
// mark the destination as offline. At this point we should attempt // mark the destination as offline. At this point we should attempt
// to send messages to the user's async relay servers if we know them. // to send messages to the user's async relay servers if we know them.
FailuresUntilAssumedOffline uint32 FailuresUntilAssumedOffline uint32
enableRelays bool
} }
func NewStatistics( func NewStatistics(
db storage.Database, db storage.Database,
failuresUntilBlacklist uint32, failuresUntilBlacklist uint32,
failuresUntilAssumedOffline uint32, failuresUntilAssumedOffline uint32,
enableRelays bool,
) Statistics { ) Statistics {
return Statistics{ return Statistics{
DB: db, DB: db,
@ -47,6 +50,7 @@ func NewStatistics(
FailuresUntilAssumedOffline: failuresUntilAssumedOffline, FailuresUntilAssumedOffline: failuresUntilAssumedOffline,
backoffTimers: make(map[spec.ServerName]*time.Timer), backoffTimers: make(map[spec.ServerName]*time.Timer),
servers: make(map[spec.ServerName]*ServerStatistics), servers: make(map[spec.ServerName]*ServerStatistics),
enableRelays: enableRelays,
} }
} }
@ -73,6 +77,13 @@ func (s *Statistics) ForServer(serverName spec.ServerName) *ServerStatistics {
} else { } else {
server.blacklisted.Store(blacklisted) server.blacklisted.Store(blacklisted)
} }
// Don't bother hitting the database 2 additional times
// if we don't want to use relays.
if !s.enableRelays {
return server
}
assumedOffline, err := s.DB.IsServerAssumedOffline(context.Background(), serverName) assumedOffline, err := s.DB.IsServerAssumedOffline(context.Background(), serverName)
if err != nil { if err != nil {
logrus.WithError(err).Errorf("Failed to get assumed offline entry %q", serverName) logrus.WithError(err).Errorf("Failed to get assumed offline entry %q", serverName)

View file

@ -16,7 +16,7 @@ const (
) )
func TestBackoff(t *testing.T) { func TestBackoff(t *testing.T) {
stats := NewStatistics(nil, FailuresUntilBlacklist, FailuresUntilAssumedOffline) stats := NewStatistics(nil, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
server := ServerStatistics{ server := ServerStatistics{
statistics: &stats, statistics: &stats,
serverName: "test.com", serverName: "test.com",
@ -106,7 +106,7 @@ func TestBackoff(t *testing.T) {
} }
func TestRelayServersListing(t *testing.T) { func TestRelayServersListing(t *testing.T) {
stats := NewStatistics(test.NewInMemoryFederationDatabase(), FailuresUntilBlacklist, FailuresUntilAssumedOffline) stats := NewStatistics(test.NewInMemoryFederationDatabase(), FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
server := ServerStatistics{statistics: &stats} server := ServerStatistics{statistics: &stats}
server.AddRelayServers([]spec.ServerName{"relay1", "relay1", "relay2"}) server.AddRelayServers([]spec.ServerName{"relay1", "relay1", "relay2"})
relayServers := server.KnownRelayServers() relayServers := server.KnownRelayServers()

View file

@ -1,9 +1,10 @@
apiVersion: v2 apiVersion: v2
name: dendrite name: dendrite
version: "0.13.7" version: "0.14.0"
appVersion: "0.13.6" appVersion: "0.13.6"
description: Dendrite Matrix Homeserver description: Dendrite Matrix Homeserver
type: application type: application
icon: https://avatars.githubusercontent.com/u/8418310?s=48&v=4
keywords: keywords:
- matrix - matrix
- chat - chat
@ -13,7 +14,7 @@ home: https://github.com/matrix-org/dendrite
sources: sources:
- https://github.com/matrix-org/dendrite - https://github.com/matrix-org/dendrite
dependencies: dependencies:
- name: postgresql - name: postgresql
version: 12.1.7 version: 14.2.3
repository: https://charts.bitnami.com/bitnami repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled condition: postgresql.enabled

View file

@ -1,7 +1,7 @@
# dendrite # dendrite
![Version: 0.13.7](https://img.shields.io/badge/Version-0.13.7-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.13.6](https://img.shields.io/badge/AppVersion-0.13.6-informational?style=flat-square) ![Version: 0.14.0](https://img.shields.io/badge/Version-0.14.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.13.6](https://img.shields.io/badge/AppVersion-0.13.6-informational?style=flat-square)
Dendrite Matrix Homeserver Dendrite Matrix Homeserver
Status: **NOT PRODUCTION READY** Status: **NOT PRODUCTION READY**
@ -37,7 +37,7 @@ Create a folder `appservices` and place your configurations in there. The confi
| Repository | Name | Version | | Repository | Name | Version |
|------------|------|---------| |------------|------|---------|
| https://charts.bitnami.com/bitnami | postgresql | 12.1.7 | | https://charts.bitnami.com/bitnami | postgresql | 14.2.3 |
## Values ## Values
| Key | Type | Default | Description | | Key | Type | Default | Description |
@ -48,16 +48,19 @@ Create a folder `appservices` and place your configurations in there. The confi
| signing_key.create | bool | `true` | Create a new signing key, if not exists | | signing_key.create | bool | `true` | Create a new signing key, if not exists |
| signing_key.existingSecret | string | `""` | Use an existing secret | | signing_key.existingSecret | string | `""` | Use an existing secret |
| resources | object | sets some sane default values | Default resource requests/limits. | | resources | object | sets some sane default values | Default resource requests/limits. |
| persistence.jetstream | object | `{"capacity":"1Gi","existingClaim":""}` | The storage class to use for volume claims. Used unless specified at the specific component. Defaults to the cluster default storage class. # If defined, storageClassName: <storageClass> # If set to "-", storageClassName: "", which disables dynamic provisioning # If undefined (the default) or set to null, no storageClassName spec is # set, choosing the default provisioner. (gp2 on AWS, standard on # GKE, AWS & OpenStack) # storageClass: "" | | persistence.storageClass | string | `nil` | The storage class to use for volume claims. Used unless specified at the specific component. Defaults to the cluster default storage class. If defined, storageClassName: <storageClass> If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. (gp2 on AWS, standard on GKE, AWS & OpenStack) |
| persistence.jetstream.existingClaim | string | `""` | Use an existing volume claim for jetstream | | persistence.jetstream.existingClaim | string | `""` | Use an existing volume claim for jetstream |
| persistence.jetstream.capacity | string | `"1Gi"` | PVC Storage Request for the jetstream volume | | persistence.jetstream.capacity | string | `"1Gi"` | PVC Storage Request for the jetstream volume |
| persistence.jetstream.storageClass | string | `nil` | The storage class to use for volume claims. Defaults to persistence.storageClass If defined, storageClassName: <storageClass> If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. (gp2 on AWS, standard on GKE, AWS & OpenStack) |
| persistence.media.existingClaim | string | `""` | Use an existing volume claim for media files | | persistence.media.existingClaim | string | `""` | Use an existing volume claim for media files |
| persistence.media.capacity | string | `"1Gi"` | PVC Storage Request for the media volume | | persistence.media.capacity | string | `"1Gi"` | PVC Storage Request for the media volume |
| persistence.media.storageClass | string | `nil` | The storage class to use for volume claims. Defaults to persistence.storageClass If defined, storageClassName: <storageClass> If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. (gp2 on AWS, standard on GKE, AWS & OpenStack) |
| persistence.search.existingClaim | string | `""` | Use an existing volume claim for the fulltext search index | | persistence.search.existingClaim | string | `""` | Use an existing volume claim for the fulltext search index |
| persistence.search.capacity | string | `"1Gi"` | PVC Storage Request for the search volume | | persistence.search.capacity | string | `"1Gi"` | PVC Storage Request for the search volume |
| persistence.search.storageClass | string | `nil` | The storage class to use for volume claims. Defaults to persistence.storageClass If defined, storageClassName: <storageClass> If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. (gp2 on AWS, standard on GKE, AWS & OpenStack) |
| extraVolumes | list | `[]` | Add additional volumes to the Dendrite Pod | | extraVolumes | list | `[]` | Add additional volumes to the Dendrite Pod |
| extraVolumeMounts | list | `[]` | Configure additional mount points volumes in the Dendrite Pod | | extraVolumeMounts | list | `[]` | Configure additional mount points volumes in the Dendrite Pod |
| strategy.type | string | `"RollingUpdate"` | Strategy to use for rolling updates (e.g. Recreate, RollingUpdate) If you are using ReadWriteOnce volumes, you should probably use Recreate | | strategy.type | string | `"Recreate"` | Strategy to use for rolling updates (e.g. Recreate, RollingUpdate) If you are using ReadWriteOnce volumes, you should probably use Recreate |
| strategy.rollingUpdate.maxUnavailable | string | `"25%"` | Maximum number of pods that can be unavailable during the update process | | strategy.rollingUpdate.maxUnavailable | string | `"25%"` | Maximum number of pods that can be unavailable during the update process |
| strategy.rollingUpdate.maxSurge | string | `"25%"` | Maximum number of pods that can be scheduled above the desired number of pods | | strategy.rollingUpdate.maxSurge | string | `"25%"` | Maximum number of pods that can be scheduled above the desired number of pods |
| dendrite_config.version | int | `2` | | | dendrite_config.version | int | `2` | |
@ -139,7 +142,7 @@ Create a folder `appservices` and place your configurations in there. The confi
| dendrite_config.logging | list | `[{"level":"info","type":"std"}]` | Default logging configuration | | dendrite_config.logging | list | `[{"level":"info","type":"std"}]` | Default logging configuration |
| postgresql.enabled | bool | See value.yaml | Enable and configure postgres as the database for dendrite. | | postgresql.enabled | bool | See value.yaml | Enable and configure postgres as the database for dendrite. |
| postgresql.image.repository | string | `"bitnami/postgresql"` | | | postgresql.image.repository | string | `"bitnami/postgresql"` | |
| postgresql.image.tag | string | `"15.1.0"` | | | postgresql.image.tag | string | `"16.2.0"` | |
| postgresql.auth.username | string | `"dendrite"` | | | postgresql.auth.username | string | `"dendrite"` | |
| postgresql.auth.password | string | `"changeme"` | | | postgresql.auth.password | string | `"changeme"` | |
| postgresql.auth.database | string | `"dendrite"` | | | postgresql.auth.database | string | `"dendrite"` | |
@ -186,3 +189,5 @@ grafana:
``` ```
PS: The label `release=kube-prometheus-stack` is setup with the helmchart of the Prometheus Operator. For Grafana Dashboards it may be necessary to enable scanning in the correct namespaces (or ALL), enabled by `sidecar.dashboards.searchNamespace` in [Helmchart of grafana](https://artifacthub.io/packages/helm/grafana/grafana) (which is part of PrometheusOperator, so `grafana.sidecar.dashboards.searchNamespace`) PS: The label `release=kube-prometheus-stack` is setup with the helmchart of the Prometheus Operator. For Grafana Dashboards it may be necessary to enable scanning in the correct namespaces (or ALL), enabled by `sidecar.dashboards.searchNamespace` in [Helmchart of grafana](https://artifacthub.io/packages/helm/grafana/grafana) (which is part of PrometheusOperator, so `grafana.sidecar.dashboards.searchNamespace`)
----------------------------------------------
Autogenerated from chart metadata using [helm-docs v1.13.0](https://github.com/norwoodj/helm-docs/releases/v1.13.0)

View file

@ -4,6 +4,7 @@
{{- $wellKnownServerHost := default $serverNameHost (regexFind "^(\\[.+\\])?[^:]*" .Values.dendrite_config.global.well_known_server_name) -}} {{- $wellKnownServerHost := default $serverNameHost (regexFind "^(\\[.+\\])?[^:]*" .Values.dendrite_config.global.well_known_server_name) -}}
{{- $wellKnownClientHost := default $serverNameHost (regexFind "//(\\[.+\\])?[^:/]*" .Values.dendrite_config.global.well_known_client_name | trimAll "/") -}} {{- $wellKnownClientHost := default $serverNameHost (regexFind "//(\\[.+\\])?[^:/]*" .Values.dendrite_config.global.well_known_client_name | trimAll "/") -}}
{{- $allHosts := list $serverNameHost $wellKnownServerHost $wellKnownClientHost | uniq -}} {{- $allHosts := list $serverNameHost $wellKnownServerHost $wellKnownClientHost | uniq -}}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
@ -56,7 +57,7 @@ spec:
service: service:
name: {{ $fullName }} name: {{ $fullName }}
port: port:
name: http number: {{ $.Values.service.port }}
{{- else }} {{- else }}
serviceName: {{ $fullName }} serviceName: {{ $fullName }}
servicePort: http servicePort: http
@ -72,7 +73,7 @@ spec:
service: service:
name: {{ $fullName }} name: {{ $fullName }}
port: port:
name: http number: {{ $.Values.service.port }}
{{- else }} {{- else }}
serviceName: {{ $fullName }} serviceName: {{ $fullName }}
servicePort: http servicePort: http
@ -88,7 +89,7 @@ spec:
service: service:
name: {{ $fullName }} name: {{ $fullName }}
port: port:
name: http number: {{ $.Values.service.port }}
{{- else }} {{- else }}
serviceName: {{ $fullName }} serviceName: {{ $fullName }}
servicePort: http servicePort: http
@ -105,7 +106,7 @@ spec:
service: service:
name: {{ $fullName }} name: {{ $fullName }}
port: port:
name: http number: {{ $.Values.service.port }}
{{- else }} {{- else }}
serviceName: {{ $fullName }} serviceName: {{ $fullName }}
servicePort: http servicePort: http

View file

@ -14,4 +14,4 @@ spec:
- name: http - name: http
protocol: TCP protocol: TCP
port: {{ .Values.service.port }} port: {{ .Values.service.port }}
targetPort: 8008 targetPort: http

View file

@ -26,13 +26,13 @@ persistence:
# -- The storage class to use for volume claims. # -- The storage class to use for volume claims.
# Used unless specified at the specific component. # Used unless specified at the specific component.
# Defaults to the cluster default storage class. # Defaults to the cluster default storage class.
## If defined, storageClassName: <storageClass> # If defined, storageClassName: <storageClass>
## If set to "-", storageClassName: "", which disables dynamic provisioning # If set to "-", storageClassName: "", which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is # If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner. (gp2 on AWS, standard on # set, choosing the default provisioner. (gp2 on AWS, standard on
## GKE, AWS & OpenStack) # GKE, AWS & OpenStack)
## #
# storageClass: "" storageClass:
jetstream: jetstream:
# -- Use an existing volume claim for jetstream # -- Use an existing volume claim for jetstream
existingClaim: "" existingClaim: ""
@ -40,13 +40,12 @@ persistence:
capacity: "1Gi" capacity: "1Gi"
# -- The storage class to use for volume claims. # -- The storage class to use for volume claims.
# Defaults to persistence.storageClass # Defaults to persistence.storageClass
## If defined, storageClassName: <storageClass> # If defined, storageClassName: <storageClass>
## If set to "-", storageClassName: "", which disables dynamic provisioning # If set to "-", storageClassName: "", which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is # If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner. (gp2 on AWS, standard on # set, choosing the default provisioner. (gp2 on AWS, standard on
## GKE, AWS & OpenStack) # GKE, AWS & OpenStack)
## storageClass:
# storageClass: ""
media: media:
# -- Use an existing volume claim for media files # -- Use an existing volume claim for media files
existingClaim: "" existingClaim: ""
@ -54,13 +53,12 @@ persistence:
capacity: "1Gi" capacity: "1Gi"
# -- The storage class to use for volume claims. # -- The storage class to use for volume claims.
# Defaults to persistence.storageClass # Defaults to persistence.storageClass
## If defined, storageClassName: <storageClass> # If defined, storageClassName: <storageClass>
## If set to "-", storageClassName: "", which disables dynamic provisioning # If set to "-", storageClassName: "", which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is # If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner. (gp2 on AWS, standard on # set, choosing the default provisioner. (gp2 on AWS, standard on
## GKE, AWS & OpenStack) # GKE, AWS & OpenStack)
## storageClass:
# storageClass: ""
search: search:
# -- Use an existing volume claim for the fulltext search index # -- Use an existing volume claim for the fulltext search index
existingClaim: "" existingClaim: ""
@ -68,13 +66,12 @@ persistence:
capacity: "1Gi" capacity: "1Gi"
# -- The storage class to use for volume claims. # -- The storage class to use for volume claims.
# Defaults to persistence.storageClass # Defaults to persistence.storageClass
## If defined, storageClassName: <storageClass> # If defined, storageClassName: <storageClass>
## If set to "-", storageClassName: "", which disables dynamic provisioning # If set to "-", storageClassName: "", which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is # If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner. (gp2 on AWS, standard on # set, choosing the default provisioner. (gp2 on AWS, standard on
## GKE, AWS & OpenStack) # GKE, AWS & OpenStack)
## storageClass:
# storageClass: ""
# -- Add additional volumes to the Dendrite Pod # -- Add additional volumes to the Dendrite Pod
extraVolumes: [] extraVolumes: []
@ -92,7 +89,7 @@ extraVolumeMounts: []
strategy: strategy:
# -- Strategy to use for rolling updates (e.g. Recreate, RollingUpdate) # -- Strategy to use for rolling updates (e.g. Recreate, RollingUpdate)
# If you are using ReadWriteOnce volumes, you should probably use Recreate # If you are using ReadWriteOnce volumes, you should probably use Recreate
type: RollingUpdate type: Recreate
rollingUpdate: rollingUpdate:
# -- Maximum number of pods that can be unavailable during the update process # -- Maximum number of pods that can be unavailable during the update process
maxUnavailable: 25% maxUnavailable: 25%
@ -378,7 +375,7 @@ postgresql:
enabled: false enabled: false
image: image:
repository: bitnami/postgresql repository: bitnami/postgresql
tag: "15.1.0" tag: "16.2.0"
auth: auth:
username: dendrite username: dendrite
password: changeme password: changeme

View file

@ -43,13 +43,19 @@ type ServerACLDatabase interface {
type ServerACLs struct { type ServerACLs struct {
acls map[string]*serverACL // room ID -> ACL acls map[string]*serverACL // room ID -> ACL
aclsMutex sync.RWMutex // protects the above aclsMutex sync.RWMutex // protects the above
aclRegexCache map[string]**regexp.Regexp // Cache from "serverName" -> pointer to a regex
aclRegexCacheMutex sync.RWMutex // protects the above
} }
func NewServerACLs(db ServerACLDatabase) *ServerACLs { func NewServerACLs(db ServerACLDatabase) *ServerACLs {
ctx := context.TODO() ctx := context.TODO()
acls := &ServerACLs{ acls := &ServerACLs{
acls: make(map[string]*serverACL), acls: make(map[string]*serverACL),
// Be generous when creating the cache, as in reality
// there are hundreds of servers in an ACL.
aclRegexCache: make(map[string]**regexp.Regexp, 100),
} }
// Look up all of the rooms that the current state server knows about. // Look up all of the rooms that the current state server knows about.
rooms, err := db.GetKnownRooms(ctx) rooms, err := db.GetKnownRooms(ctx)
if err != nil { if err != nil {
@ -67,6 +73,7 @@ func NewServerACLs(db ServerACLDatabase) *ServerACLs {
for _, event := range events { for _, event := range events {
acls.OnServerACLUpdate(event) acls.OnServerACLUpdate(event)
} }
return acls return acls
} }
@ -78,8 +85,8 @@ type ServerACL struct {
type serverACL struct { type serverACL struct {
ServerACL ServerACL
allowedRegexes []*regexp.Regexp allowedRegexes []**regexp.Regexp
deniedRegexes []*regexp.Regexp deniedRegexes []**regexp.Regexp
} }
func compileACLRegex(orig string) (*regexp.Regexp, error) { func compileACLRegex(orig string) (*regexp.Regexp, error) {
@ -89,6 +96,25 @@ func compileACLRegex(orig string) (*regexp.Regexp, error) {
return regexp.Compile(escaped) return regexp.Compile(escaped)
} }
// cachedCompileACLRegex is a wrapper around compileACLRegex with added caching
func (s *ServerACLs) cachedCompileACLRegex(orig string) (**regexp.Regexp, error) {
s.aclRegexCacheMutex.RLock()
re, ok := s.aclRegexCache[orig]
if ok {
s.aclRegexCacheMutex.RUnlock()
return re, nil
}
s.aclRegexCacheMutex.RUnlock()
compiled, err := compileACLRegex(orig)
if err != nil {
return nil, err
}
s.aclRegexCacheMutex.Lock()
defer s.aclRegexCacheMutex.Unlock()
s.aclRegexCache[orig] = &compiled
return &compiled, nil
}
func (s *ServerACLs) OnServerACLUpdate(strippedEvent tables.StrippedEvent) { func (s *ServerACLs) OnServerACLUpdate(strippedEvent tables.StrippedEvent) {
acls := &serverACL{} acls := &serverACL{}
if err := json.Unmarshal([]byte(strippedEvent.ContentValue), &acls.ServerACL); err != nil { if err := json.Unmarshal([]byte(strippedEvent.ContentValue), &acls.ServerACL); err != nil {
@ -100,14 +126,14 @@ func (s *ServerACLs) OnServerACLUpdate(strippedEvent tables.StrippedEvent) {
// special characters and then replace * and ? with their regex counterparts. // special characters and then replace * and ? with their regex counterparts.
// https://matrix.org/docs/spec/client_server/r0.6.1#m-room-server-acl // https://matrix.org/docs/spec/client_server/r0.6.1#m-room-server-acl
for _, orig := range acls.Allowed { for _, orig := range acls.Allowed {
if expr, err := compileACLRegex(orig); err != nil { if expr, err := s.cachedCompileACLRegex(orig); err != nil {
logrus.WithError(err).Errorf("Failed to compile allowed regex") logrus.WithError(err).Errorf("Failed to compile allowed regex")
} else { } else {
acls.allowedRegexes = append(acls.allowedRegexes, expr) acls.allowedRegexes = append(acls.allowedRegexes, expr)
} }
} }
for _, orig := range acls.Denied { for _, orig := range acls.Denied {
if expr, err := compileACLRegex(orig); err != nil { if expr, err := s.cachedCompileACLRegex(orig); err != nil {
logrus.WithError(err).Errorf("Failed to compile denied regex") logrus.WithError(err).Errorf("Failed to compile denied regex")
} else { } else {
acls.deniedRegexes = append(acls.deniedRegexes, expr) acls.deniedRegexes = append(acls.deniedRegexes, expr)
@ -118,6 +144,11 @@ func (s *ServerACLs) OnServerACLUpdate(strippedEvent tables.StrippedEvent) {
"num_allowed": len(acls.allowedRegexes), "num_allowed": len(acls.allowedRegexes),
"num_denied": len(acls.deniedRegexes), "num_denied": len(acls.deniedRegexes),
}).Debugf("Updating server ACLs for %q", strippedEvent.RoomID) }).Debugf("Updating server ACLs for %q", strippedEvent.RoomID)
// Clear out Denied and Allowed, now that we have the compiled regexes.
// They are not needed anymore from this point on.
acls.Denied = nil
acls.Allowed = nil
s.aclsMutex.Lock() s.aclsMutex.Lock()
defer s.aclsMutex.Unlock() defer s.aclsMutex.Unlock()
s.acls[strippedEvent.RoomID] = acls s.acls[strippedEvent.RoomID] = acls
@ -150,14 +181,14 @@ func (s *ServerACLs) IsServerBannedFromRoom(serverName spec.ServerName, roomID s
// Check if the hostname matches one of the denied regexes. If it does then // Check if the hostname matches one of the denied regexes. If it does then
// the server is banned from the room. // the server is banned from the room.
for _, expr := range acls.deniedRegexes { for _, expr := range acls.deniedRegexes {
if expr.MatchString(string(serverName)) { if (*expr).MatchString(string(serverName)) {
return true return true
} }
} }
// Check if the hostname matches one of the allowed regexes. If it does then // Check if the hostname matches one of the allowed regexes. If it does then
// the server is NOT banned from the room. // the server is NOT banned from the room.
for _, expr := range acls.allowedRegexes { for _, expr := range acls.allowedRegexes {
if expr.MatchString(string(serverName)) { if (*expr).MatchString(string(serverName)) {
return false return false
} }
} }

View file

@ -15,8 +15,14 @@
package acls package acls
import ( import (
"context"
"regexp" "regexp"
"testing" "testing"
"github.com/matrix-org/dendrite/roomserver/storage/tables"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/stretchr/testify/assert"
) )
func TestOpenACLsWithBlacklist(t *testing.T) { func TestOpenACLsWithBlacklist(t *testing.T) {
@ -38,8 +44,8 @@ func TestOpenACLsWithBlacklist(t *testing.T) {
ServerACL: ServerACL{ ServerACL: ServerACL{
AllowIPLiterals: true, AllowIPLiterals: true,
}, },
allowedRegexes: []*regexp.Regexp{allowRegex}, allowedRegexes: []**regexp.Regexp{&allowRegex},
deniedRegexes: []*regexp.Regexp{denyRegex}, deniedRegexes: []**regexp.Regexp{&denyRegex},
} }
if acls.IsServerBannedFromRoom("1.2.3.4", roomID) { if acls.IsServerBannedFromRoom("1.2.3.4", roomID) {
@ -77,8 +83,8 @@ func TestDefaultACLsWithWhitelist(t *testing.T) {
ServerACL: ServerACL{ ServerACL: ServerACL{
AllowIPLiterals: false, AllowIPLiterals: false,
}, },
allowedRegexes: []*regexp.Regexp{allowRegex}, allowedRegexes: []**regexp.Regexp{&allowRegex},
deniedRegexes: []*regexp.Regexp{}, deniedRegexes: []**regexp.Regexp{},
} }
if !acls.IsServerBannedFromRoom("1.2.3.4", roomID) { if !acls.IsServerBannedFromRoom("1.2.3.4", roomID) {
@ -103,3 +109,45 @@ func TestDefaultACLsWithWhitelist(t *testing.T) {
t.Fatal("Expected qux.com:4567 to be allowed but wasn't") t.Fatal("Expected qux.com:4567 to be allowed but wasn't")
} }
} }
var (
content1 = `{"allow":["*"],"allow_ip_literals":false,"deny":["hello.world", "*.hello.world"]}`
)
type dummyACLDB struct{}
func (d dummyACLDB) GetKnownRooms(ctx context.Context) ([]string, error) {
return []string{"1", "2"}, nil
}
func (d dummyACLDB) GetBulkStateContent(ctx context.Context, roomIDs []string, tuples []gomatrixserverlib.StateKeyTuple, allowWildcards bool) ([]tables.StrippedEvent, error) {
return []tables.StrippedEvent{
{
RoomID: "1",
ContentValue: content1,
},
{
RoomID: "2",
ContentValue: content1,
},
}, nil
}
func TestCachedRegex(t *testing.T) {
db := dummyACLDB{}
wantBannedServer := spec.ServerName("hello.world")
acls := NewServerACLs(db)
// Check that hello.world is banned in room 1
banned := acls.IsServerBannedFromRoom(wantBannedServer, "1")
assert.True(t, banned)
// Check that hello.world is banned in room 2
banned = acls.IsServerBannedFromRoom(wantBannedServer, "2")
assert.True(t, banned)
// Check that matrix.hello.world is banned in room 2
banned = acls.IsServerBannedFromRoom("matrix."+wantBannedServer, "2")
assert.True(t, banned)
}

View file

@ -18,6 +18,13 @@ type FederationAPI struct {
// The default value is 16 if not specified, which is circa 18 hours. // The default value is 16 if not specified, which is circa 18 hours.
FederationMaxRetries uint32 `yaml:"send_max_retries"` FederationMaxRetries uint32 `yaml:"send_max_retries"`
// P2P Feature: Whether relaying to specific nodes should be enabled.
// Defaults to false.
// Note: Enabling relays introduces a huge startup delay, if you are not using
// relays and have many servers to re-hydrate on start. Only enable this
// if you are using relays!
EnableRelays bool `yaml:"enable_relays"`
// P2P Feature: How many consecutive failures that we should tolerate when // P2P Feature: How many consecutive failures that we should tolerate when
// sending federation requests to a specific server until we should assume they // sending federation requests to a specific server until we should assume they
// are offline. If we assume they are offline then we will attempt to send // are offline. If we assume they are offline then we will attempt to send