From 5829872bec9b9986c741eafec36e47774e4d2b3e Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 11 Nov 2020 15:07:34 +0000 Subject: [PATCH] Fix port script to handle foreign key constraints (#8730) --- .buildkite/test_db.db | Bin 19279872 -> 19296256 bytes changelog.d/8730.bugfix | 1 + scripts/synapse_port_db | 68 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 changelog.d/8730.bugfix diff --git a/.buildkite/test_db.db b/.buildkite/test_db.db index 361369a581771bed36692a848aa396df96ad59d9..a0d9f16a7522c17f91ace4175a2234b3f6a93941 100644 GIT binary patch delta 2680 zcmajf3v?7$9mnyV&0~{Yb|(pBlR{wc5|BhfD1uroN-1n z57Z_hv4dKmiB_jo(=@hJ@jo zKmK%;GOCP;iK5P6N$INtMIMjR)}J9a_Fp$}SRFg8Dvg5$iu$rg9UJO2n{T@Xwp(bs zKHDv_-D2DI+it*ir`c}Mc0;zS+HQ&MmfCKa?M^@J&d9$$FRTnZK2iRJ*W+<6PAb3ssnnIfy_Yin*x9Jj}=IumClv#p|&Uby$RY zEXKuHf;V6(mf?+f6W)wV@D{ukmtr~IhPPt{F2hP(j#XHV7_Pt?T!{v}18Z>=)~uoL|lzz1Sf&Nqh>Q#xU;02=2ltK7+e)5BA|+d={U>etaHZz!&i)9Kd}zi2Lzn9Kr+m z3JzloU&Vv?8orK)a0K7LI9z-a-@>=?FplCo_%6PO@8c0XipTH+9K#RsBRq~L@MHW0 zKgG}RBz}(LIDx0|G=70+@GK^963^jz{1PwVSNJtv#BcCh{0_gzOL!T7z$-X~)A%F) zgfsXv&f+ilEB=O8@pt?K|HQv;In<-v3K?D*c{1{46v!x);geA$qgaMtMnJ|i89^B# z8LEsD8KpAHWK5SaLq@rbu#5^Bl`<}pF;m7Y8C5c7%b25#b2})FQ8jqNGb3-FM?I=u z6&eg}8Y#LfqKMg^BcbJ?MAT(a~47 zTq#H2q?RmUaPCF!oI1I`dKPv4zBL|oU+7ryrD@Mia{|u*Q%aWU_NIXnU&Z3Z-rbus+O~u~ zWyq8lWpQWrX{E9*TU6AroJEU7rcKXXkT6WKy+gKcne~3*42}C|t`kkMD`HKt##OP6 zxnt9!CTEh`x(fvxRtu|Tvb5Z}+?tdeeK;6iom+BpcjX>rw;-oZI+N11_N`VbuBQyM z^?XsiQ#UhI3Oy2x_A={+riOJZo3@Cxu`U0#q&jiFW_D?tY1J|pWN4x}p`~=8i54rR z8?C0eQQsjfQ~d9QZ91d2>CIY4TBjc^vmh?ob(Up*dxfd%agnivZm^dr(XJV0 z20}AwoF9^?(YuqnnKnB0T4AMF0`pFunMgM?$ZE+}%h}6wN7U7(oAjCzUGj_)dWIF7 zVzPTtqb1UoNLsxa_QKE-i5=p?G@P6~+Xfa9j~ntV;8YwT~

*<#tIY0v99bekq!?skh#IylQlibtL-rL zK=yVsG1u7I&>GB+wFZrul~$iB)l5goDAHZEmSR<-%wW3LXO0UQd*hSZ{3@hk>*pCg zspiDeiY$Ftc!AA3roar=89uYdWwAwjH~W>*&`~G6@F53%kc+g0Qo3D zA&x;224WD3F&M{U2ug4qj>icYic*x}M4W_SI2otlR1C)mjKpaeh0{@vGjJx(!r3?n z6&Q_kF$QCC9x5>o<8eMNzywUhBuvJIxCm2lF{&^X({Kr@F&&rUGR(l`xB@eAC1znZ z=HMz^jcaf%Y7oL)%)@n#?iX~Wz2Hb|*aR-*+ zPTYk?G@%(SxEuFiIaXjLOsv9c+>1515BH-LQLM!Sco6Hb9uMJRY``OU6dSP#kKu8& zVKdtC1fE0(w%{prB8I2Yg=g?Ao@-PH)U2wck`+%HLOR{bZ=RiB~lVC;navGakaOx-LD$k0|=r&1|VOxwHK)Re*u2p B#UKCx diff --git a/changelog.d/8730.bugfix b/changelog.d/8730.bugfix new file mode 100644 index 000000000..dcc42bc98 --- /dev/null +++ b/changelog.d/8730.bugfix @@ -0,0 +1 @@ +Fix port script to correctly handle foreign key constraints. Broke in v1.21.0. diff --git a/scripts/synapse_port_db b/scripts/synapse_port_db index 13c0120bb..7a638ea8e 100755 --- a/scripts/synapse_port_db +++ b/scripts/synapse_port_db @@ -22,7 +22,7 @@ import logging import sys import time import traceback -from typing import Optional +from typing import Dict, Optional, Set import yaml @@ -292,6 +292,34 @@ class Porter(object): return table, already_ported, total_to_port, forward_chunk, backward_chunk + async def get_table_constraints(self) -> Dict[str, Set[str]]: + """Returns a map of tables that have foreign key constraints to tables they depend on. + """ + + def _get_constraints(txn): + # We can pull the information about foreign key constraints out from + # the postgres schema tables. + sql = """ + SELECT DISTINCT + tc.table_name, + ccu.table_name AS foreign_table_name + FROM + information_schema.table_constraints AS tc + INNER JOIN information_schema.constraint_column_usage AS ccu + USING (table_schema, constraint_name) + WHERE tc.constraint_type = 'FOREIGN KEY'; + """ + txn.execute(sql) + + results = {} + for table, foreign_table in txn: + results.setdefault(table, set()).add(foreign_table) + return results + + return await self.postgres_store.db_pool.runInteraction( + "get_table_constraints", _get_constraints + ) + async def handle_table( self, table, postgres_size, table_size, forward_chunk, backward_chunk ): @@ -619,15 +647,43 @@ class Porter(object): consumeErrors=True, ) ) + # Map from table name to args passed to `handle_table`, i.e. a tuple + # of: `postgres_size`, `table_size`, `forward_chunk`, `backward_chunk`. + tables_to_port_info_map = {r[0]: r[1:] for r in setup_res} # Step 4. Do the copying. + # + # This is slightly convoluted as we need to ensure tables are ported + # in the correct order due to foreign key constraints. self.progress.set_state("Copying to postgres") - await make_deferred_yieldable( - defer.gatherResults( - [run_in_background(self.handle_table, *res) for res in setup_res], - consumeErrors=True, + + constraints = await self.get_table_constraints() + tables_ported = set() # type: Set[str] + + while tables_to_port_info_map: + # Pulls out all tables that are still to be ported and which + # only depend on tables that are already ported (if any). + tables_to_port = [ + table + for table in tables_to_port_info_map + if not constraints.get(table, set()) - tables_ported + ] + + await make_deferred_yieldable( + defer.gatherResults( + [ + run_in_background( + self.handle_table, + table, + *tables_to_port_info_map.pop(table), + ) + for table in tables_to_port + ], + consumeErrors=True, + ) ) - ) + + tables_ported.update(tables_to_port) # Step 5. Set up sequences self.progress.set_state("Setting up sequence generators")