File 0955-Fix-for-delete_object-at-read-on-a-set-table.patch of Package erlang
From b1dd0efb565f3eac1e3dbc4b18c2001ad53ea2ef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Magnus=20Fr=C3=B6berg?= <magnus@klarna.com>
Date: Thu, 11 Jun 2020 16:14:26 +0200
Subject: [PATCH] Fix for delete_object at read on a set table On a set table,
if the delete_object doesnt delete (due to no match) at commit return
whatever is (or is not) stored in source table as a result of a read in the
same transaction as the delete_object. And, if a delete already has been done
on Oid in the same transaction do not record a delete_object operation on the
same Oid. Added delete_object tests on set table and made the existing tests
checking more.
---
lib/mnesia/src/mnesia.erl | 22 ++--
lib/mnesia/test/mnesia_trans_access_test.erl | 102 +++++++++++++++++--
2 files changed, 110 insertions(+), 14 deletions(-)
diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl
index 3f1b173e37..2efe739442 100644
--- a/lib/mnesia/src/mnesia.erl
+++ b/lib/mnesia/src/mnesia.erl
@@ -788,7 +788,8 @@ do_delete_object(Tid, Ts, Tab, Val, LockKind) ->
?ets_match_delete(Store, {Oid, Val, '_'}),
?ets_insert(Store, {Oid, Val, delete_object});
_ ->
- case ?ets_match_object(Store, {Oid, '_', write}) of
+ case ?ets_match_object(Store, {Oid, '_', write}) ++
+ ?ets_match_object(Store, {Oid, '_', delete}) of
[] ->
?ets_match_delete(Store, {Oid, Val, '_'}),
?ets_insert(Store, {Oid, Val, delete_object});
@@ -1221,19 +1222,28 @@ add_written(Written, Tab, ObjsFun, LockKind) ->
add_written_to_bag(Written, ObjsFun(), []);
_ when LockKind == read;
LockKind == write ->
- add_written_to_set(Written);
+ add_written_to_set(Written, ObjsFun);
_ ->
- _ = ObjsFun(), % Fall back to request new lock and read from source
- add_written_to_set(Written)
+ %% Fall back to request new lock and read from source
+ add_written_to_set(Written, ObjsFun())
end.
-add_written_to_set(Ws) ->
+add_written_to_set(Ws, ObjsOrFun) ->
case lists:last(Ws) of
{_, _, delete} -> [];
{_, Val, write} -> [Val];
- {_, _, delete_object} -> []
+ {Oid, _, delete_object} ->
+ %% May be several 'delete_object' in Ws; need to check if any
+ %% deleted Val exists in source table; if not return whatever
+ %% is/is not in the source table (ie as the Val is only deleted
+ %% if matched at commit this needs to be reflected here)
+ [Val || Val <- get_objs(ObjsOrFun),
+ not lists:member({Oid, Val, delete_object}, Ws)]
end.
+get_objs(ObjsFun) when is_function(ObjsFun) -> ObjsFun();
+get_objs(Objs) when is_list(Objs) -> Objs.
+
add_written_to_bag([{_, Val, write} | Tail], Objs, Ack) ->
add_written_to_bag(Tail, lists:delete(Val, Objs), [Val | Ack]);
add_written_to_bag([], Objs, Ack) ->
diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl
index 723a85fd2c..f488134391 100644
--- a/lib/mnesia/test/mnesia_trans_access_test.erl
+++ b/lib/mnesia/test/mnesia_trans_access_test.erl
@@ -63,7 +64,7 @@ end_per_testcase(Func, Conf) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
all() ->
- [write, read, wread, delete, delete_object,
+ [write, read, wread, delete, delete_object_bag, delete_object_set,
match_object, select, select14, all_keys, transaction,
{group, nested_activities}, {group, index_tabs},
{group, index_lifecycle}].
@@ -221,14 +222,26 @@ delete(Config) when is_list(Config) ->
%% Delete matching record
-delete_object(suite) -> [];
-delete_object(Config) when is_list(Config) ->
+delete_object_bag(suite) -> [];
+delete_object_bag(Config) when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
+ ?match(ok, delete_object(Node1, bag)),
+ ?verify_mnesia(Nodes, []).
+
+delete_object_set(suite) -> [];
+delete_object_set(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ ?match(ok, delete_object(Node1, set)),
+ ?verify_mnesia(Nodes, []).
+
+delete_object(Node1, Type) ->
Tab = delete_object,
- Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
+ Schema = [{name, Tab}, {type, Type},
+ {attributes, [k, v]}, {ram_copies, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
OneRec = {Tab, 1, 2},
+ OtherRec = {Tab, 1, 3},
?match({aborted, {bad_type, _}},
mnesia:transaction(fun() -> mnesia:delete_object([]) end)),
?match({aborted, {bad_type, _}},
@@ -237,16 +250,88 @@ delete_object(Config) when is_list(Config) ->
mnesia:transaction(fun() -> mnesia:delete_object({Tab, 1}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
+
+ %% Delete already existing object
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
?match({atomic, ok},
- mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
+ mnesia:transaction(fun() ->
+ [OneRec] = mnesia:read(Tab, 1),
+ ok = mnesia:delete_object(OneRec),
+ [] = mnesia:read(Tab, 1),
+ ok
+ end)),
+ ?match([], mnesia:dirty_read(Tab, 1)),
+
+ %% Delete already existing object (written twice)
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
?match({atomic, ok},
- mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
+ mnesia:transaction(fun() ->
+ [OneRec] = mnesia:read(Tab, 1),
+ ok = mnesia:delete_object(OneRec),
+ [] = mnesia:read(Tab, 1),
+ ok
+ end)),
+ ?match([], mnesia:dirty_read(Tab, 1)),
+
+ %% Delete object written in same transaction
+ ?match({atomic, ok},
+ mnesia:transaction(fun() ->
+ [] = mnesia:read(Tab, 1),
+ ok = mnesia:write(OneRec),
+ ok = mnesia:delete_object(OneRec),
+ [] = mnesia:read(Tab, 1),
+ ok
+ end)),
+ ?match([], mnesia:dirty_read(Tab, 1)),
+
+ %% Delete other object than written in same transaction
+ ?match({atomic, ok},
+ mnesia:transaction(fun() ->
+ [] = mnesia:read(Tab, 1),
+ ok = mnesia:write(OneRec),
+ ok = mnesia:delete_object(OtherRec),
+ [OneRec] = mnesia:read(Tab, 1),
+ ok
+ end)),
+ ?match([OneRec], mnesia:dirty_read(Tab, 1)),
+
+ %% Delete other object than already existing
+ ?match({atomic, ok},
+ mnesia:transaction(fun() ->
+ [OneRec] = mnesia:read(Tab, 1),
+ ok = mnesia:delete_object(OtherRec),
+ [OneRec] = mnesia:read(Tab, 1),
+ ok
+ end)),
+ ?match([OneRec], mnesia:dirty_read(Tab, 1)),
+
+ %% Delete object in combination with delete
+ ?match({atomic, ok},
+ mnesia:transaction(fun() ->
+ [OneRec] = mnesia:read(Tab, 1),
+ ok = mnesia:delete({Tab, 1}),
+ ok = mnesia:delete_object(OtherRec),
+ [] = mnesia:read(Tab, 1),
+ ok
+ end)),
+ ?match([], mnesia:dirty_read(Tab, 1)),
+
+ %% Several delete_object in same transaction (last on non existing record)
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() ->
+ [OneRec] = mnesia:read(Tab, 1),
+ ok = mnesia:delete_object(OneRec),
+ ok = mnesia:delete_object(OtherRec),
+ [] = mnesia:read(Tab, 1),
+ ok
+ end)),
+ ?match([], mnesia:dirty_read(Tab, 1)),
?match({'EXIT', {aborted, no_transaction}}, mnesia:delete_object(OneRec)),
@@ -255,7 +340,8 @@ delete_object(Config) when is_list(Config) ->
?match({aborted, {bad_type, Tab, _}},
mnesia:transaction(fun() -> mnesia:delete_object({Tab, {['$5']}, 21}) end)),
- ?verify_mnesia(Nodes, []).
+ ?match({atomic, ok}, mnesia:delete_table(Tab)),
+ ok.
%% Read matching records
--
2.26.2