Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Warn if postgres database has non-C locale. #6734

Merged
merged 11 commits into from
Jan 28, 2020
9 changes: 9 additions & 0 deletions UPGRADE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ for example:
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb
Upgrading to **<NEXT_VERSION>**
===============================

Synapse will now log a warning on start up if used with a PostgreSQL database
that has a non-recommended locale set.

See [docs/postgres.md](docs/postgres.md) for details.


Upgrading to v1.8.0
===================

Expand Down
1 change: 1 addition & 0 deletions changelog.d/6734.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Warn if postgres database has a non-C locale, as that can cause issues when upgrading locales (e.g. due to upgrading OS).
20 changes: 19 additions & 1 deletion docs/postgres.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Assuming your PostgreSQL database user is called `postgres`, first authenticate
su - postgres
# Or, if your system uses sudo to get administrative rights
sudo -u postgres bash

Then, create a user ``synapse_user`` with:

createuser --pwprompt synapse_user
Expand Down Expand Up @@ -63,6 +63,24 @@ You may need to enable password authentication so `synapse_user` can
connect to the database. See
<https://www.postgresql.org/docs/11/auth-pg-hba-conf.html>.

### Fixing incorrect `COLLATE` or `CTYPE`

Synapse will refuse to set up a new database if it has the wrong values of
`COLLATE` and `CTYPE` set, and will log warnings on existing databases. Using
different locales can cause issues if the locale library is updated from
underneath the database, or if a different version of the locale is used on any
replicas.

The safest way to fix the issue is to take a dump and recreate the database with
the correct `COLLATE` and `CTYPE` parameters (as per
[docs/postgres.md](docs/postgres.md)). It is also possible to change the
parameters on a live database and run a `REINDEX` on the entire database,
however extreme care must be taken to avoid database corruption.
richvdh marked this conversation as resolved.
Show resolved Hide resolved

Note that the above may fail with an error about duplicate rows if corruption
has already occurred, and such duplicate rows will need to be manually removed.


## Tuning Postgres

The default settings should be fine for most deployments. For larger
Expand Down
42 changes: 42 additions & 0 deletions synapse/storage/engines/postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import logging

from ._base import IncorrectDatabaseSetup

logger = logging.getLogger(__name__)


class PostgresEngine(object):
single_threaded = False
Expand Down Expand Up @@ -52,6 +56,44 @@ def check_database(self, db_conn, allow_outdated_version: bool = False):
"See docs/postgres.rst for more information." % (rows[0][0],)
)

txn.execute(
"SELECT datcollate, datctype FROM pg_database WHERE datname = current_database()"
)
collation, ctype = txn.fetchone()
if collation != "C":
logger.warning(
"Database has incorrect collation of %r. Should be 'C'", collation
)

if ctype != "C":
logger.warning(
"Database has incorrect ctype of %r. Should be 'C'", ctype
)

def check_new_database(self, txn):
"""Gets called when setting up a brand new database. This allows us to
apply stricter checks on new databases versus existing database.
"""

txn.execute(
"SELECT datcollate, datctype FROM pg_database WHERE datname = current_database()"
)
collation, ctype = txn.fetchone()

errors = []

if collation != "C":
errors.append(" - 'COLLATE' is set to %r. Should be 'C'" % (collation,))

if ctype != "C":
errors.append(" - 'CTYPE' is set to %r. Should be 'C'" % (collation,))

if errors:
raise IncorrectDatabaseSetup(
"Database is incorrectly configured:\n\n%s\n\n"
"See docs/postgres.md for more information." % ("\n".join(errors))
)

def convert_param_style(self, sql):
return sql.replace("?", "%s")

Expand Down
5 changes: 5 additions & 0 deletions synapse/storage/engines/sqlite.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ def check_database(self, db_conn, allow_outdated_version: bool = False):
if version < (3, 11, 0):
raise RuntimeError("Synapse requires sqlite 3.11 or above.")

def check_new_database(self, txn):
"""Gets called when setting up a brand new database. This allows us to
apply stricter checks on new databases versus existing database.
"""

def convert_param_style(self, sql):
return sql

Expand Down
5 changes: 5 additions & 0 deletions synapse/storage/prepare_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ def _setup_new_database(cur, database_engine, data_stores):
data_stores (list[str]): The names of the data stores to instantiate
on the given database.
"""

# We're about to set up a brand new database so we check that its
# configured to our liking.
database_engine.check_new_database(cur)

current_dir = os.path.join(dir_path, "schema", "full_schemas")
directory_entries = os.listdir(current_dir)

Expand Down