Iterate on structure

This commit is contained in:
Eric Eastwood
2026-06-04 16:31:46 -05:00
parent 67f23913ef
commit 99b13354d2
12 changed files with 1168 additions and 312 deletions
Generated
+805 -55
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -64,6 +64,12 @@ tokio = { version = "1.44.2", features = ["rt", "rt-multi-thread"] }
once_cell = "1.18.0"
itertools = "0.14.0"
# TODO: Remove: These are just used to make sure a tokio-postgres backed database pool makes sense
# with our interfaces
bb8 = "0.8.3"
bb8-postgres = "0.8.1"
postgres-native-tls = "0.5.0"
[build-dependencies]
blake2 = "0.10.4"
hex = "0.4.3"
+1
View File
@@ -43,4 +43,5 @@ pub enum RoomCreationPreset {
pub struct ExperimentalConfig {
pub msc3881_enabled: bool,
pub msc3575_enabled: bool,
pub msc4222_enabled: bool,
}
+20 -2
View File
@@ -14,17 +14,35 @@
*/
use pyo3::{
prelude::*,
types::{PyAnyMethods, PyModule, PyModuleMethods},
Bound, PyResult, Python,
};
use crate::storage::store::Store;
pub mod versions;
#[pyclass]
struct RustHandlers {
versions: versions::VersionsHandler,
}
#[pymethods]
impl RustHandlers {
#[new]
#[pyo3(signature = (homeserver))]
pub fn py_new(py: Python<'_>, homeserver: &Bound<'_, PyAny>) -> PyResult<RustHandlers> {
RustHandlers {
versions: versions::VersionsHandler { store: Store {} },
}
}
}
/// Called when registering modules with python.
pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
let child_module = PyModule::new(py, "handlers")?;
// TODO: Expose `get_versions`
// child_module.add_class::<PushRule>()?;
child_module.add_class::<RustHandlers>()?;
m.add_submodule(&child_module)?;
+148 -142
View File
@@ -26,147 +26,153 @@ struct VersionsResponse {
unstable_features: std::collections::BTreeMap<String, bool>,
}
/// Assemble a `/versions` response
async fn get_versions(
pub struct VersionsHandler {
store: &Store,
user_id: Option<&str>,
config: SynapseConfig,
) -> Result<VersionsResponse, anyhow::Error> {
let msc3881_enabled = match user_id {
Some(user_id) => {
store
.is_feature_enabled(user_id, PerUserExperimentalFeature::MSC3881)
.await?
}
None => config.experimental.msc3881_enabled,
};
let msc3575_enabled = match user_id {
Some(user_id) => {
store
.is_feature_enabled(user_id, PerUserExperimentalFeature::MSC3575)
.await?
}
None => config.experimental.msc3575_enabled,
};
// TODO: Calculate these once since they shouldn't change after start-up.
// e2ee_forced_public = (
// RoomCreationPreset.PUBLIC_CHAT
// in config.room.encryption_enabled_by_default_for_room_presets
// );
// e2ee_forced_private = (
// RoomCreationPreset.PRIVATE_CHAT
// in config.room.encryption_enabled_by_default_for_room_presets
// );
// e2ee_forced_trusted_private = (
// RoomCreationPreset.TRUSTED_PRIVATE_CHAT
// in config.room.encryption_enabled_by_default_for_room_presets
// );
return Ok(VersionsResponse {
versions: Vec::from([
// XXX: at some point we need to decide whether we need to include
// the previous version numbers, given we've defined r0.3.0 to be
// backwards compatible with r0.2.0. But need to check how
// conscientious we've been in compatibility, and decide whether the
// middle number is the major revision when at 0.X.Y (as opposed to
// X.Y.Z). And we need to decide whether it's fair to make clients
// parse the version string to figure out what's going on.
"r0.0.1".to_string(),
"r0.1.0".to_string(),
"r0.2.0".to_string(),
"r0.3.0".to_string(),
"r0.4.0".to_string(),
"r0.5.0".to_string(),
"r0.6.0".to_string(),
"r0.6.1".to_string(),
"v1.1".to_string(),
"v1.2".to_string(),
"v1.3".to_string(),
"v1.4".to_string(),
"v1.5".to_string(),
"v1.6".to_string(),
"v1.7".to_string(),
"v1.8".to_string(),
"v1.9".to_string(),
"v1.10".to_string(),
"v1.11".to_string(),
"v1.12".to_string(),
]),
unstable_features: std::collections::BTreeMap::from([
// // Implements support for label-based filtering as described in
// // MSC2326.
// ("org.matrix.label_based_filtering".to_string(), true),
// // Implements support for cross signing as described in MSC1756
// ("org.matrix.e2e_cross_signing".to_string(), true),
// // Implements additional endpoints as described in MSC2432
// ("org.matrix.msc2432".to_string(), true),
// // Implements additional endpoints as described in MSC2666
// ("uk.half-shot.msc2666.query_mutual_rooms.stable".to_string(), true),
// // Whether new rooms will be set to encrypted or not (based on presets).
// ("io.element.e2ee_forced.public".to_string(), e2ee_forced_public),
// ("io.element.e2ee_forced.private".to_string(), e2ee_forced_private),
// ("io.element.e2ee_forced.trusted_private".to_string(), e2ee_forced_trusted_private),
// // Supports the busy presence state described in MSC3026.
// ("org.matrix.msc3026.busy_presence".to_string(), config.experimental.msc3026_enabled),
// // Supports receiving private read receipts as per MSC2285
// ("org.matrix.msc2285.stable".to_string(), true), // TODO: Remove when MSC2285 becomes a part of the spec
// // Supports filtering of /publicRooms by room type as per MSC3827
// ("org.matrix.msc3827.stable".to_string(), true),
// // Adds support for thread relations, per MSC3440.
// ("org.matrix.msc3440.stable".to_string(), true), // TODO: remove when "v1.3" is added above
// // Support for thread read receipts & notification counts.
// ("org.matrix.msc3771".to_string(), true),
// ("org.matrix.msc3773".to_string(), config.experimental.msc3773_enabled),
// // Allows moderators to fetch redacted event content as described in MSC2815
// ("fi.mau.msc2815".to_string(), config.experimental.msc2815_enabled),
// // Adds a ping endpoint for appservices to check HS->AS connection
// ("fi.mau.msc2659.stable".to_string(), true), // TODO: remove when "v1.7" is added above
// // TODO: this is no longer needed once unstable MSC3882 does not need to be supported:
// ("org.matrix.msc3882".to_string(), config.auth.login_via_existing_enabled),
// Adds support for remotely enabling/disabling pushers, as per MSC3881
("org.matrix.msc3881".to_string(), msc3881_enabled),
// // Adds support for filtering /messages by event relation.
// ("org.matrix.msc3874".to_string(), config.experimental.msc3874_enabled),
// // Adds support for relation-based redactions as per MSC3912.
// ("org.matrix.msc3912".to_string(), config.experimental.msc3912_enabled),
// // Whether recursively provide relations is supported.
// // TODO This is no longer needed once unstable MSC3981 does not need to be supported.
// ("org.matrix.msc3981".to_string(), true),
// // Adds support for deleting account data.
// ("org.matrix.msc3391".to_string(), config.experimental.msc3391_enabled),
// // Allows clients to inhibit profile update propagation.
// ("org.matrix.msc4069".to_string(), config.experimental.msc4069_profile_inhibit_propagation),
// // Allows clients to handle push for encrypted events.
// ("org.matrix.msc4028".to_string(), config.experimental.msc4028_push_encrypted_events),
// // MSC4108: Mechanism to allow OIDC sign in and E2EE set up via QR code - 2024 version
// ("org.matrix.msc4108".to_string(), (
// config.experimental.msc4108_enabled
// or (
// config.experimental.msc4108_delegation_endpoint
// is not None
// )
// )),
// // MSC4140: Delayed events
// ("org.matrix.msc4140".to_string(), bool(config.server.max_event_delay_ms)),
// Simplified sliding sync
("org.matrix.simplified_msc3575".to_string(), msc3575_enabled),
// // Arbitrary key-value profile fields.
// ("uk.tcpip.msc4133".to_string(), config.experimental.msc4133_enabled),
// ("uk.tcpip.msc4133.stable".to_string(), true),
// // MSC4155: Invite filtering
// ("org.matrix.msc4155".to_string(), config.experimental.msc4155_enabled),
// // MSC4306: Support for thread subscriptions
// ("org.matrix.msc4306".to_string(), config.experimental.msc4306_enabled),
// // MSC4169: Backwards-compatible redaction sending using `/send`
// ("com.beeper.msc4169".to_string(), config.experimental.msc4169_enabled),
// // MSC4354: Sticky events
// ("org.matrix.msc4354".to_string(), config.experimental.msc4354_enabled),
// // MSC4380: Invite blocking
// ("org.matrix.msc4380.stable".to_string(), true),
// // MSC4445: Sync timeline order
// ("org.matrix.msc4445.initial_sync_timeline_topological_ordering".to_string(), true),
]),
});
}
impl VersionsHandler {
/// Assemble a `/versions` response
async fn get_versions(
&self,
user_id: Option<&str>,
config: SynapseConfig,
) -> Result<VersionsResponse, anyhow::Error> {
let msc3881_enabled = match user_id {
Some(user_id) => {
self.store
.is_feature_enabled(user_id, PerUserExperimentalFeature::MSC3881)
.await?
}
None => PerUserExperimentalFeature::MSC3881.is_globally_enabled(config),
};
let msc3575_enabled = match user_id {
Some(user_id) => {
self.store
.is_feature_enabled(user_id, PerUserExperimentalFeature::MSC3575)
.await?
}
None => PerUserExperimentalFeature::MSC3575.is_globally_enabled(config),
};
// TODO: Calculate these once since they shouldn't change after start-up.
// e2ee_forced_public = (
// RoomCreationPreset.PUBLIC_CHAT
// in config.room.encryption_enabled_by_default_for_room_presets
// );
// e2ee_forced_private = (
// RoomCreationPreset.PRIVATE_CHAT
// in config.room.encryption_enabled_by_default_for_room_presets
// );
// e2ee_forced_trusted_private = (
// RoomCreationPreset.TRUSTED_PRIVATE_CHAT
// in config.room.encryption_enabled_by_default_for_room_presets
// );
return Ok(VersionsResponse {
versions: Vec::from([
// XXX: at some point we need to decide whether we need to include
// the previous version numbers, given we've defined r0.3.0 to be
// backwards compatible with r0.2.0. But need to check how
// conscientious we've been in compatibility, and decide whether the
// middle number is the major revision when at 0.X.Y (as opposed to
// X.Y.Z). And we need to decide whether it's fair to make clients
// parse the version string to figure out what's going on.
"r0.0.1".to_string(),
"r0.1.0".to_string(),
"r0.2.0".to_string(),
"r0.3.0".to_string(),
"r0.4.0".to_string(),
"r0.5.0".to_string(),
"r0.6.0".to_string(),
"r0.6.1".to_string(),
"v1.1".to_string(),
"v1.2".to_string(),
"v1.3".to_string(),
"v1.4".to_string(),
"v1.5".to_string(),
"v1.6".to_string(),
"v1.7".to_string(),
"v1.8".to_string(),
"v1.9".to_string(),
"v1.10".to_string(),
"v1.11".to_string(),
"v1.12".to_string(),
]),
unstable_features: std::collections::BTreeMap::from([
// // Implements support for label-based filtering as described in
// // MSC2326.
// ("org.matrix.label_based_filtering".to_string(), true),
// // Implements support for cross signing as described in MSC1756
// ("org.matrix.e2e_cross_signing".to_string(), true),
// // Implements additional endpoints as described in MSC2432
// ("org.matrix.msc2432".to_string(), true),
// // Implements additional endpoints as described in MSC2666
// ("uk.half-shot.msc2666.query_mutual_rooms.stable".to_string(), true),
// // Whether new rooms will be set to encrypted or not (based on presets).
// ("io.element.e2ee_forced.public".to_string(), e2ee_forced_public),
// ("io.element.e2ee_forced.private".to_string(), e2ee_forced_private),
// ("io.element.e2ee_forced.trusted_private".to_string(), e2ee_forced_trusted_private),
// // Supports the busy presence state described in MSC3026.
// ("org.matrix.msc3026.busy_presence".to_string(), config.experimental.msc3026_enabled),
// // Supports receiving private read receipts as per MSC2285
// ("org.matrix.msc2285.stable".to_string(), true), // TODO: Remove when MSC2285 becomes a part of the spec
// // Supports filtering of /publicRooms by room type as per MSC3827
// ("org.matrix.msc3827.stable".to_string(), true),
// // Adds support for thread relations, per MSC3440.
// ("org.matrix.msc3440.stable".to_string(), true), // TODO: remove when "v1.3" is added above
// // Support for thread read receipts & notification counts.
// ("org.matrix.msc3771".to_string(), true),
// ("org.matrix.msc3773".to_string(), config.experimental.msc3773_enabled),
// // Allows moderators to fetch redacted event content as described in MSC2815
// ("fi.mau.msc2815".to_string(), config.experimental.msc2815_enabled),
// // Adds a ping endpoint for appservices to check HS->AS connection
// ("fi.mau.msc2659.stable".to_string(), true), // TODO: remove when "v1.7" is added above
// // TODO: this is no longer needed once unstable MSC3882 does not need to be supported:
// ("org.matrix.msc3882".to_string(), config.auth.login_via_existing_enabled),
// Adds support for remotely enabling/disabling pushers, as per MSC3881
("org.matrix.msc3881".to_string(), msc3881_enabled),
// // Adds support for filtering /messages by event relation.
// ("org.matrix.msc3874".to_string(), config.experimental.msc3874_enabled),
// // Adds support for relation-based redactions as per MSC3912.
// ("org.matrix.msc3912".to_string(), config.experimental.msc3912_enabled),
// // Whether recursively provide relations is supported.
// // TODO This is no longer needed once unstable MSC3981 does not need to be supported.
// ("org.matrix.msc3981".to_string(), true),
// // Adds support for deleting account data.
// ("org.matrix.msc3391".to_string(), config.experimental.msc3391_enabled),
// // Allows clients to inhibit profile update propagation.
// ("org.matrix.msc4069".to_string(), config.experimental.msc4069_profile_inhibit_propagation),
// // Allows clients to handle push for encrypted events.
// ("org.matrix.msc4028".to_string(), config.experimental.msc4028_push_encrypted_events),
// // MSC4108: Mechanism to allow OIDC sign in and E2EE set up via QR code - 2024 version
// ("org.matrix.msc4108".to_string(), (
// config.experimental.msc4108_enabled
// or (
// config.experimental.msc4108_delegation_endpoint
// is not None
// )
// )),
// // MSC4140: Delayed events
// ("org.matrix.msc4140".to_string(), bool(config.server.max_event_delay_ms)),
// Simplified sliding sync
("org.matrix.simplified_msc3575".to_string(), msc3575_enabled),
// // Arbitrary key-value profile fields.
// ("uk.tcpip.msc4133".to_string(), config.experimental.msc4133_enabled),
// ("uk.tcpip.msc4133.stable".to_string(), true),
// // MSC4155: Invite filtering
// ("org.matrix.msc4155".to_string(), config.experimental.msc4155_enabled),
// // MSC4306: Support for thread subscriptions
// ("org.matrix.msc4306".to_string(), config.experimental.msc4306_enabled),
// // MSC4169: Backwards-compatible redaction sending using `/send`
// ("com.beeper.msc4169".to_string(), config.experimental.msc4169_enabled),
// // MSC4354: Sticky events
// ("org.matrix.msc4354".to_string(), config.experimental.msc4354_enabled),
// // MSC4380: Invite blocking
// ("org.matrix.msc4380.stable".to_string(), true),
// // MSC4445: Sync timeline order
// ("org.matrix.msc4445.initial_sync_timeline_topological_ordering".to_string(), true),
]),
});
}
}
-72
View File
@@ -1,72 +0,0 @@
/*
* This file is licensed under the Affero General Public License (AGPL) version 3.
*
* Copyright (C) 2026 Element Creations Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* See the GNU Affero General Public License for more details:
* <https://www.gnu.org/licenses/agpl-3.0.html>.
*
*/
use crate::db::python_db_pool::DatabasePool as PythonDatabasePool;
/// A [`tokio_postgres::Transaction`] looking thing that we can use on the Rust side to
/// interact with the database
pub trait Transaction {
pub fn query(&self, sql: &str, args: &[&str]) -> None {
todo!("TODO");
}
}
/// Database access backed by the database pool we already have in the Python side of Synapse
struct SynapsePythonTransaction {
db_pool: PythonDatabasePool,
}
impl Transaction for SynapsePythonTransaction {
fn query(&self, sql: &str, args: &[&str]) -> None {
todo!("TODO");
}
}
/// Native Rust database access backed by `tokio-postgres` (for use in synapse-rust-apps)
// struct TokioPostgresTransaction {
// db_pool: bb8::Pool<PostgresConnectionManager<MakeTlsConnector>>,
// }
// impl Transaction for TokioPostgresTransaction {
// fn query(&self, sql: &str, args: &[&str]) -> None {
// todo!("TODO");
// // TODO: Set isolation level
// let mut conn = self
// .db_pool
// .get()
// .instrument(tracing::info_span!("acquire database connection"))
// .await
// .map_err(|e| {
// sentry::capture_error(&e);
// tracing::error!(
// error = e.to_string(),
// "Failed to acquire database connection"
// );
// anyhow::anyhow!("Failed to acquire database connection: {e}")
// })?;
// let txn = conn
// .transaction()
// .instrument(tracing::info_span!("start transaction"))
// .await
// .context("Failed to start transaction")?;
// let rows = txn.query(sql, args).await?;
// rows
// }
// }
+11 -1
View File
@@ -14,5 +14,15 @@
*/
pub mod python_db_pool;
pub mod rust_db_pool;
pub mod db;
pub trait DatabasePool {
async fn get_transaction(&self, description: &str) -> dyn Transaction;
}
/// A [`tokio_postgres::Transaction`] looking thing that we can use on the Rust side to
/// interact with the database
pub trait Transaction {
async fn query(&self, sql: &str, args: &[&str]) -> ();
async fn commit(&self) -> ();
}
+56 -34
View File
@@ -15,6 +15,8 @@
use pyo3::{intern, prelude::*};
use crate::storage::db::{DatabasePool, Transaction};
/// The database engines we support in the Python side of Synapse
#[derive(Copy, Clone, Debug)]
pub enum DatabaseEngine {
@@ -32,59 +34,79 @@ impl DatabaseEngine {
}
}
/// Wrapper for a `DatabasePool` from the Python side of Synapse.
pub struct DatabasePool<'py> {
/// The underlying `DatabasePool`
raw: Bound<'py, PyAny>,
}
pub struct PythonDatabasePool {}
impl<'py> DatabasePool<'py> {
pub fn get_transaction(&mut self, description: &str) -> LoggingTransactionWrapper {
todo!("TODO");
impl DatabasePool for PythonDatabasePool {
pub fn get_transaction(&self, description: &str) -> dyn Transaction {
todo!("...");
// let execute_fn = self.raw.getattr(intern!(self.raw.py(), "runInteraction"))?;
// execute_fn.call1((sql, args))?;
// Ok(())
}
}
/// Wrapper for a `LoggingTransaction` from the Python side of Synapse.
pub struct LoggingTransactionWrapper<'py> {
/// The underlying `LoggingTransaction`
raw: Bound<'py, PyAny>,
fn detect_engine(txn_py: &Bound<'_, PyAny>) -> PyResult<DatabaseEngine> {
let name = txn_py
.getattr("database_engine")
.expect("`LoggingTransaction` must have `database_engine` attr")
.get_type()
.name()
.expect("`database_engine` type must have a name")
.to_str()
.expect("`database_engine` type name must be valid UTF-8")
.to_owned();
/// Dissambiguate which underyling database engine we're working with
database_engine: DatabaseEngine,
Ok(match name.as_str() {
"PostgresEngine" => DatabaseEngine::Postgres,
"Sqlite3Engine" => DatabaseEngine::Sqlite,
other => unimplemented!(
"Unknown database engine {other:?}. This is a Synapse programming error."
),
})
}
impl<'py> FromPyObject<'_, 'py> for LoggingTransactionWrapper<'py> {
/// Wrapper for a `LoggingTransaction` from the Python side of Synapse.
///
/// Holds no `'py` lifetime so it can be stored and moved freely across threads.
/// Use [`execute`](Self::execute) (or other methods) while holding the GIL.
pub struct LoggingTransactionWrapper {
/// The underlying `LoggingTransaction`
raw: Py<PyAny>,
/// Disambiguate which underlying database engine we're working with
pub database_engine: DatabaseEngine,
}
impl<'a, 'py> FromPyObject<'a, 'py> for LoggingTransactionWrapper {
type Error = PyErr;
/// From Python `LoggingTransaction`
fn extract(logging_transaction_python_object: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
let database_engine = match logging_transaction_python_object
.getattr("database_engine")
.expect("Expected the Python object you passed to be `LoggingTransaction` which should have `database_engine` attr")
.get_type()
.name()
.expect("Expected `LoggingTransaction.database_engine` to have a type name")
.to_str()
.expect("Expected to be able to convert the `LoggingTransaction.database_engine` type name to a string")
{
"PostgresEngine" => DatabaseEngine::Postgres,
"Sqlite3Engine" => DatabaseEngine::Sqlite,
other => unimplemented!("Unknown database engine {other:?}. This is a Synapse programming error."),
};
/// Extract from a Python `LoggingTransaction` passed as an argument.
///
/// The resulting wrapper has `done_tx = None`; Python owns the transaction lifetime.
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
let database_engine = detect_engine(&obj.to_owned())?;
Ok(Self {
raw: logging_transaction_python_object.cast()?.to_owned(),
raw: obj.to_owned().unbind(),
database_engine,
})
}
}
impl<'py> LoggingTransactionWrapper<'py> {
pub fn execute(&mut self, sql: &str, args: &'py Bound<'py, PyAny>) -> PyResult<()> {
let execute_fn = self.raw.getattr(intern!(self.raw.py(), "execute"))?;
impl LoggingTransactionWrapper {
pub fn execute<'py>(
&mut self,
py: Python<'py>,
sql: &str,
args: &Bound<'py, PyAny>,
) -> PyResult<()> {
let execute_fn = self.raw.bind(py).getattr(intern!(py, "execute"))?;
execute_fn.call1((sql, args))?;
Ok(())
}
}
impl Transaction for LoggingTransactionWrapper {
fn query(&self, sql: &str, args: &[&str]) -> () {
self.execute(sql, args);
}
}
+71
View File
@@ -0,0 +1,71 @@
/*
* This file is licensed under the Affero General Public License (AGPL) version 3.
*
* Copyright (C) 2026 Element Creations Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* See the GNU Affero General Public License for more details:
* <https://www.gnu.org/licenses/agpl-3.0.html>.
*
*/
// TODO: remove. This is just here to make sure our `DatabasePool`/`Transaction`
// interfaces are compatible with `tokio-postgres`.
use anyhow::Context;
use bb8_postgres::{tokio_postgres::Row, PostgresConnectionManager};
use postgres_native_tls::MakeTlsConnector;
use crate::storage::db::{DatabasePool, Transaction};
/// Native Rust database access backed by `tokio-postgres` (for use in synapse-rust-apps)
pub struct RustDatabasePool {
db_pool: bb8::Pool<PostgresConnectionManager<MakeTlsConnector>>,
}
impl DatabasePool for RustDatabasePool {
async fn get_transaction(&self, description: &str) -> dyn Transaction {
let mut conn = self
.db_pool
.get()
// .instrument(tracing::info_span!("acquire database connection"))
.await
.context("Failed to acquire database connection")?;
let txn = conn
.transaction()
// .instrument(tracing::info_span!("start transaction"))
.await
.context("Failed to start transaction")?;
// TODO: Set isolation level
txn
}
}
struct TokioPostgresTransaction<'a> {
txn: bb8_postgres::tokio_postgres::Transaction<'a>,
}
impl Transaction for TokioPostgresTransaction<'_> {
async fn query(&self, sql: &str, args: &[&str]) -> () {
todo!("TODO");
let rows = self.txn.query(sql, args).await?;
rows
}
async fn commit(&self) -> () {
self.txn
.commit()
// .instrument(tracing::info_span!("commit transaction"))
.await
.context("Failed to commit transaction")?;
}
}
+41 -2
View File
@@ -13,6 +13,10 @@
*
*/
use sha2::digest::typenum::Len;
use crate::{config::SynapseConfig, storage::db::DatabasePool};
/// Currently supported per-user features
pub enum PerUserExperimentalFeature {
MSC3881,
@@ -20,7 +24,20 @@ pub enum PerUserExperimentalFeature {
MSC4222,
}
pub struct Store {}
impl PerUserExperimentalFeature {
pub fn is_globally_enabled(&self, config: SynapseConfig) -> bool {
match self {
PerUserExperimentalFeature::MSC3881 => config.experimental.msc3881_enabled,
PerUserExperimentalFeature::MSC3575 => config.experimental.msc3575_enabled,
PerUserExperimentalFeature::MSC4222 => config.experimental.msc4222_enabled,
}
}
}
pub struct Store {
config: SynapseConfig,
db_pool: dyn DatabasePool,
}
impl Store {
pub async fn is_feature_enabled(
@@ -28,6 +45,28 @@ impl Store {
user_id: &str,
feature: PerUserExperimentalFeature,
) -> Result<bool, anyhow::Error> {
todo!("...");
if feature.is_globally_enabled(self.config) {
return Ok(true);
}
let txn = self.db_pool.get_transaction("is_feature_enabled").await;
let rows = txn
.query(
r#"
SELECT enabled
FROM per_user_experimental_features
WHERE user_id = ? AND feature = ?
"#,
&[user_id, feature],
)
.await;
match (rows.len(), rows.first()) {
(1, Some(enabled)) => Ok(enabled),
(0, None) => Ok(false),
_ => {
panic!("Synapse programming error");
}
}
}
}
+5 -4
View File
@@ -25,13 +25,11 @@ import logging
import re
from typing import TYPE_CHECKING
from synapse.api.constants import RoomCreationPreset
from synapse.http.server import HttpServer
from synapse.http.servlet import RestServlet
from synapse.http.site import SynapseRequest
from synapse.rest.admin.experimental_features import ExperimentalFeature
from synapse.types import JsonDict
from synapse.synapse_rust.handlers.versions import get_versions
from synapse.types import JsonDict
if TYPE_CHECKING:
from synapse.server import HomeServer
@@ -48,6 +46,7 @@ class VersionsRestServlet(RestServlet):
self.config = hs.config
self.auth = hs.get_auth()
self.store = hs.get_datastores().main
self.rust_handlers = hs.get_rust_handlers()
async def on_GET(self, request: SynapseRequest) -> tuple[int, JsonDict]:
user_id = None
@@ -80,7 +79,9 @@ class VersionsRestServlet(RestServlet):
# authenticated responses are not served from cache.
request.setHeader(b"Vary", b"Authorization")
versions_response_body = await get_versions(user_id, self.config)
versions_response_body = await self.rust_handlers.versions.get_versions(
user_id, self.config
)
return (
200,
+4
View File
@@ -963,6 +963,10 @@ class HomeServer(metaclass=abc.ABCMeta):
def get_set_password_handler(self) -> SetPasswordHandler:
return SetPasswordHandler(self)
@cache_in_self
def get_rust_handlers(self) -> RustHandlers:
return RustHandlers(self)
@cache_in_self
def get_event_sources(self) -> EventSources:
return EventSources(self)