@@ -1129,7 +1129,6 @@ async def simple_upsert(
1129
1129
values : Dict [str , Any ],
1130
1130
insertion_values : Optional [Dict [str , Any ]] = None ,
1131
1131
desc : str = "simple_upsert" ,
1132
- lock : bool = True ,
1133
1132
) -> bool :
1134
1133
"""Insert a row with values + insertion_values; on conflict, update with values.
1135
1134
@@ -1154,21 +1153,12 @@ async def simple_upsert(
1154
1153
requiring that a unique index exist on the column names used to detect a
1155
1154
conflict (i.e. `keyvalues.keys()`).
1156
1155
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.
1172
1162
1173
1163
[*]: Some tables have unique indices added to them in the background. Those
1174
1164
tables `T` are keys in the dictionary UNIQUE_INDEX_BACKGROUND_UPDATES,
@@ -1189,7 +1179,6 @@ async def simple_upsert(
1189
1179
values: The nonunique columns and their new values
1190
1180
insertion_values: additional key/values to use only when inserting
1191
1181
desc: description of the transaction, for logging and metrics
1192
- lock: True to lock the table when doing the upsert.
1193
1182
Returns:
1194
1183
Returns True if a row was inserted or updated (i.e. if `values` is
1195
1184
not empty then this always returns True)
@@ -1209,7 +1198,6 @@ async def simple_upsert(
1209
1198
keyvalues ,
1210
1199
values ,
1211
1200
insertion_values ,
1212
- lock = lock ,
1213
1201
db_autocommit = autocommit ,
1214
1202
)
1215
1203
except self .engine .module .IntegrityError as e :
@@ -1232,7 +1220,6 @@ def simple_upsert_txn(
1232
1220
values : Dict [str , Any ],
1233
1221
insertion_values : Optional [Dict [str , Any ]] = None ,
1234
1222
where_clause : Optional [str ] = None ,
1235
- lock : bool = True ,
1236
1223
) -> bool :
1237
1224
"""
1238
1225
Pick the UPSERT method which works best on the platform. Either the
@@ -1245,8 +1232,6 @@ def simple_upsert_txn(
1245
1232
values: The nonunique columns and their new values
1246
1233
insertion_values: additional key/values to use only when inserting
1247
1234
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.
1250
1235
Returns:
1251
1236
Returns True if a row was inserted or updated (i.e. if `values` is
1252
1237
not empty then this always returns True)
@@ -1270,7 +1255,6 @@ def simple_upsert_txn(
1270
1255
values ,
1271
1256
insertion_values = insertion_values ,
1272
1257
where_clause = where_clause ,
1273
- lock = lock ,
1274
1258
)
1275
1259
1276
1260
def simple_upsert_txn_emulated (
@@ -1291,14 +1275,15 @@ def simple_upsert_txn_emulated(
1291
1275
insertion_values: additional key/values to use only when inserting
1292
1276
where_clause: An index predicate to apply to the upsert.
1293
1277
lock: True to lock the table when doing the upsert.
1278
+ Must not be False unless the table has already been locked.
1294
1279
Returns:
1295
1280
Returns True if a row was inserted or updated (i.e. if `values` is
1296
1281
not empty then this always returns True)
1297
1282
"""
1298
1283
insertion_values = insertion_values or {}
1299
1284
1300
- # We need to lock the table :(, unless we're *really* careful
1301
1285
if lock :
1286
+ # We need to lock the table :(
1302
1287
self .engine .lock_table (txn , table )
1303
1288
1304
1289
def _getwhere (key : str ) -> str :
@@ -1406,7 +1391,6 @@ async def simple_upsert_many(
1406
1391
value_names : Collection [str ],
1407
1392
value_values : Collection [Collection [Any ]],
1408
1393
desc : str ,
1409
- lock : bool = True ,
1410
1394
) -> None :
1411
1395
"""
1412
1396
Upsert, many times.
@@ -1418,8 +1402,6 @@ async def simple_upsert_many(
1418
1402
value_names: The value column names
1419
1403
value_values: A list of each row's value column values.
1420
1404
Ignored if value_names is empty.
1421
- lock: True to lock the table when doing the upsert. Unused when performing
1422
- a native upsert.
1423
1405
"""
1424
1406
1425
1407
# We can autocommit if it safe to upsert
@@ -1433,7 +1415,6 @@ async def simple_upsert_many(
1433
1415
key_values ,
1434
1416
value_names ,
1435
1417
value_values ,
1436
- lock = lock ,
1437
1418
db_autocommit = autocommit ,
1438
1419
)
1439
1420
@@ -1445,7 +1426,6 @@ def simple_upsert_many_txn(
1445
1426
key_values : Collection [Iterable [Any ]],
1446
1427
value_names : Collection [str ],
1447
1428
value_values : Iterable [Iterable [Any ]],
1448
- lock : bool = True ,
1449
1429
) -> None :
1450
1430
"""
1451
1431
Upsert, many times.
@@ -1457,16 +1437,19 @@ def simple_upsert_many_txn(
1457
1437
value_names: The value column names
1458
1438
value_values: A list of each row's value column values.
1459
1439
Ignored if value_names is empty.
1460
- lock: True to lock the table when doing the upsert. Unused when performing
1461
- a native upsert.
1462
1440
"""
1463
1441
if table not in self ._unsafe_to_upsert_tables :
1464
1442
return self .simple_upsert_many_txn_native_upsert (
1465
1443
txn , table , key_names , key_values , value_names , value_values
1466
1444
)
1467
1445
else :
1468
1446
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 ,
1470
1453
)
1471
1454
1472
1455
def simple_upsert_many_txn_emulated (
@@ -1477,7 +1460,6 @@ def simple_upsert_many_txn_emulated(
1477
1460
key_values : Collection [Iterable [Any ]],
1478
1461
value_names : Collection [str ],
1479
1462
value_values : Iterable [Iterable [Any ]],
1480
- lock : bool = True ,
1481
1463
) -> None :
1482
1464
"""
1483
1465
Upsert, many times, but without native UPSERT support or batching.
@@ -1489,18 +1471,16 @@ def simple_upsert_many_txn_emulated(
1489
1471
value_names: The value column names
1490
1472
value_values: A list of each row's value column values.
1491
1473
Ignored if value_names is empty.
1492
- lock: True to lock the table when doing the upsert.
1493
1474
"""
1494
1475
# No value columns, therefore make a blank list so that the following
1495
1476
# zip() works correctly.
1496
1477
if not value_names :
1497
1478
value_values = [() for x in range (len (key_values ))]
1498
1479
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" )
1504
1484
1505
1485
for keyv , valv in zip (key_values , value_values ):
1506
1486
_keys = {x : y for x , y in zip (key_names , keyv )}
0 commit comments