@@ -1129,7 +1129,6 @@ async def simple_upsert(
11291129 values : Dict [str , Any ],
11301130 insertion_values : Optional [Dict [str , Any ]] = None ,
11311131 desc : str = "simple_upsert" ,
1132- lock : bool = True ,
11331132 ) -> bool :
11341133 """Insert a row with values + insertion_values; on conflict, update with values.
11351134
@@ -1154,21 +1153,12 @@ async def simple_upsert(
11541153 requiring that a unique index exist on the column names used to detect a
11551154 conflict (i.e. `keyvalues.keys()`).
11561155
1157- If there is no such index, we can "emulate" an upsert with a SELECT followed
1158- by either an INSERT or an UPDATE. This is unsafe: we cannot make the same
1159- atomicity guarantees that a native upsert can and are very vulnerable to races
1160- and crashes. Therefore if we wish to upsert without an appropriate unique index,
1161- we must either:
1162-
1163- 1. Acquire a table-level lock before the emulated upsert (`lock=True`), or
1164- 2. VERY CAREFULLY ensure that we are the only thread and worker which will be
1165- writing to this table, in which case we can proceed without a lock
1166- (`lock=False`).
1167-
1168- Generally speaking, you should use `lock=True`. If the table in question has a
1169- unique index[*], this class will use a native upsert (which is atomic and so can
1170- ignore the `lock` argument). Otherwise this class will use an emulated upsert,
1171- in which case we want the safer option unless we been VERY CAREFUL.
1156+ If there is no such index yet[*], we can "emulate" an upsert with a SELECT
1157+ followed by either an INSERT or an UPDATE. This is unsafe unless *all* upserters
1158+ run at the SERIALIZABLE isolation level: we cannot make the same atomicity
1159+ guarantees that a native upsert can and are very vulnerable to races and
1160+ crashes. Therefore to upsert without an appropriate unique index, we acquire a
1161+ table-level lock before the emulated upsert.
11721162
11731163 [*]: Some tables have unique indices added to them in the background. Those
11741164 tables `T` are keys in the dictionary UNIQUE_INDEX_BACKGROUND_UPDATES,
@@ -1189,7 +1179,6 @@ async def simple_upsert(
11891179 values: The nonunique columns and their new values
11901180 insertion_values: additional key/values to use only when inserting
11911181 desc: description of the transaction, for logging and metrics
1192- lock: True to lock the table when doing the upsert.
11931182 Returns:
11941183 Returns True if a row was inserted or updated (i.e. if `values` is
11951184 not empty then this always returns True)
@@ -1209,7 +1198,6 @@ async def simple_upsert(
12091198 keyvalues ,
12101199 values ,
12111200 insertion_values ,
1212- lock = lock ,
12131201 db_autocommit = autocommit ,
12141202 )
12151203 except self .engine .module .IntegrityError as e :
@@ -1232,7 +1220,6 @@ def simple_upsert_txn(
12321220 values : Dict [str , Any ],
12331221 insertion_values : Optional [Dict [str , Any ]] = None ,
12341222 where_clause : Optional [str ] = None ,
1235- lock : bool = True ,
12361223 ) -> bool :
12371224 """
12381225 Pick the UPSERT method which works best on the platform. Either the
@@ -1245,8 +1232,6 @@ def simple_upsert_txn(
12451232 values: The nonunique columns and their new values
12461233 insertion_values: additional key/values to use only when inserting
12471234 where_clause: An index predicate to apply to the upsert.
1248- lock: True to lock the table when doing the upsert. Unused when performing
1249- a native upsert.
12501235 Returns:
12511236 Returns True if a row was inserted or updated (i.e. if `values` is
12521237 not empty then this always returns True)
@@ -1270,7 +1255,6 @@ def simple_upsert_txn(
12701255 values ,
12711256 insertion_values = insertion_values ,
12721257 where_clause = where_clause ,
1273- lock = lock ,
12741258 )
12751259
12761260 def simple_upsert_txn_emulated (
@@ -1291,14 +1275,15 @@ def simple_upsert_txn_emulated(
12911275 insertion_values: additional key/values to use only when inserting
12921276 where_clause: An index predicate to apply to the upsert.
12931277 lock: True to lock the table when doing the upsert.
1278+ Must not be False unless the table has already been locked.
12941279 Returns:
12951280 Returns True if a row was inserted or updated (i.e. if `values` is
12961281 not empty then this always returns True)
12971282 """
12981283 insertion_values = insertion_values or {}
12991284
1300- # We need to lock the table :(, unless we're *really* careful
13011285 if lock :
1286+ # We need to lock the table :(
13021287 self .engine .lock_table (txn , table )
13031288
13041289 def _getwhere (key : str ) -> str :
@@ -1406,7 +1391,6 @@ async def simple_upsert_many(
14061391 value_names : Collection [str ],
14071392 value_values : Collection [Collection [Any ]],
14081393 desc : str ,
1409- lock : bool = True ,
14101394 ) -> None :
14111395 """
14121396 Upsert, many times.
@@ -1418,8 +1402,6 @@ async def simple_upsert_many(
14181402 value_names: The value column names
14191403 value_values: A list of each row's value column values.
14201404 Ignored if value_names is empty.
1421- lock: True to lock the table when doing the upsert. Unused when performing
1422- a native upsert.
14231405 """
14241406
14251407 # We can autocommit if it safe to upsert
@@ -1433,7 +1415,6 @@ async def simple_upsert_many(
14331415 key_values ,
14341416 value_names ,
14351417 value_values ,
1436- lock = lock ,
14371418 db_autocommit = autocommit ,
14381419 )
14391420
@@ -1445,7 +1426,6 @@ def simple_upsert_many_txn(
14451426 key_values : Collection [Iterable [Any ]],
14461427 value_names : Collection [str ],
14471428 value_values : Iterable [Iterable [Any ]],
1448- lock : bool = True ,
14491429 ) -> None :
14501430 """
14511431 Upsert, many times.
@@ -1457,16 +1437,19 @@ def simple_upsert_many_txn(
14571437 value_names: The value column names
14581438 value_values: A list of each row's value column values.
14591439 Ignored if value_names is empty.
1460- lock: True to lock the table when doing the upsert. Unused when performing
1461- a native upsert.
14621440 """
14631441 if table not in self ._unsafe_to_upsert_tables :
14641442 return self .simple_upsert_many_txn_native_upsert (
14651443 txn , table , key_names , key_values , value_names , value_values
14661444 )
14671445 else :
14681446 return self .simple_upsert_many_txn_emulated (
1469- txn , table , key_names , key_values , value_names , value_values , lock = lock
1447+ txn ,
1448+ table ,
1449+ key_names ,
1450+ key_values ,
1451+ value_names ,
1452+ value_values ,
14701453 )
14711454
14721455 def simple_upsert_many_txn_emulated (
@@ -1477,7 +1460,6 @@ def simple_upsert_many_txn_emulated(
14771460 key_values : Collection [Iterable [Any ]],
14781461 value_names : Collection [str ],
14791462 value_values : Iterable [Iterable [Any ]],
1480- lock : bool = True ,
14811463 ) -> None :
14821464 """
14831465 Upsert, many times, but without native UPSERT support or batching.
@@ -1489,18 +1471,16 @@ def simple_upsert_many_txn_emulated(
14891471 value_names: The value column names
14901472 value_values: A list of each row's value column values.
14911473 Ignored if value_names is empty.
1492- lock: True to lock the table when doing the upsert.
14931474 """
14941475 # No value columns, therefore make a blank list so that the following
14951476 # zip() works correctly.
14961477 if not value_names :
14971478 value_values = [() for x in range (len (key_values ))]
14981479
1499- if lock :
1500- # Lock the table just once, to prevent it being done once per row.
1501- # Note that, according to Postgres' documentation, once obtained,
1502- # the lock is held for the remainder of the current transaction.
1503- self .engine .lock_table (txn , "user_ips" )
1480+ # Lock the table just once, to prevent it being done once per row.
1481+ # Note that, according to Postgres' documentation, once obtained,
1482+ # the lock is held for the remainder of the current transaction.
1483+ self .engine .lock_table (txn , "user_ips" )
15041484
15051485 for keyv , valv in zip (key_values , value_values ):
15061486 _keys = {x : y for x , y in zip (key_names , keyv )}
0 commit comments