@@ -82,6 +82,7 @@ rdbms_queries_cases() ->
82
82
test_request_transaction ,
83
83
test_restart_transaction_with_execute ,
84
84
test_restart_transaction_with_execute_eventually_passes ,
85
+ prepare_without_execute_does_not_cause_inconsistency ,
85
86
test_failed_transaction_with_execute_wrapped ,
86
87
test_failed_wrapper_transaction ,
87
88
test_incremental_upsert ,
@@ -104,8 +105,7 @@ init_per_suite(Config) ->
104
105
orelse mongoose_helper :is_rdbms_enabled (host_type ()) of
105
106
false -> {skip , rdbms_or_ct_not_running };
106
107
true ->
107
- % % Warning: inject_module does not really work well with --rerun-big-tests flag
108
- mongoose_helper :inject_module (? MODULE ),
108
+ mongoose_helper :inject_module (? MODULE , reload ),
109
109
Config1 = mongoose_helper :backup_and_set_config_option (Config , [instrumentation , probe_interval ], 1 ),
110
110
escalus :init_per_suite (Config1 )
111
111
end .
@@ -115,15 +115,15 @@ end_per_suite(Config) ->
115
115
escalus :end_per_suite (Config ).
116
116
117
117
init_per_group (tagged_rdbms_queries , Config ) ->
118
- ExtraConfig = stop_global_default_pool (),
119
- instrument_helper :start (declared_events (tagged_rdbms_queries )),
118
+ ExtraConfig = stop_global_default_pool () ++ [{ scope , host_type ()}, { tag , tag ()}] ,
119
+ instrument_helper :start (declared_events (ExtraConfig )),
120
120
start_local_host_type_pool (ExtraConfig ),
121
121
ExtraConfig ++ Config ;
122
122
init_per_group (global_rdbms_queries , Config ) ->
123
- ExtraConfig = stop_global_default_pool (),
124
- instrument_helper :start (declared_events (global_rdbms_queries )),
123
+ ExtraConfig = stop_global_default_pool () ++ [{ scope , global }, { tag , default }] ,
124
+ instrument_helper :start (declared_events (ExtraConfig )),
125
125
restart_global_default_pool (ExtraConfig ),
126
- [{ tag , global } | Config ] .
126
+ ExtraConfig ++ Config .
127
127
128
128
end_per_group (tagged_rdbms_queries , Config ) ->
129
129
stop_local_host_type_pool (),
@@ -136,6 +136,14 @@ end_per_group(global_rdbms_queries, Config) ->
136
136
init_per_testcase (test_incremental_upsert , Config ) ->
137
137
erase_inbox (Config ),
138
138
escalus :init_per_testcase (test_incremental_upsert , Config );
139
+ init_per_testcase (Case = prepare_without_execute_does_not_cause_inconsistency , Config ) ->
140
+ case is_pgsql () orelse is_cockroachdb () of
141
+ true ->
142
+ erase_inbox (Config ),
143
+ escalus :init_per_testcase (Case , Config );
144
+ false ->
145
+ {skip , " Test for a Postgres-specific issue" }
146
+ end ;
139
147
init_per_testcase (CaseName , Config ) ->
140
148
escalus :init_per_testcase (CaseName , Config ).
141
149
@@ -147,18 +155,25 @@ end_per_testcase(CaseName, Config)
147
155
CaseName =:= test_failed_wrapper_transaction ->
148
156
rpc (mim (), meck , unload , []),
149
157
escalus :end_per_testcase (CaseName , Config );
158
+ end_per_testcase (Case = prepare_without_execute_does_not_cause_inconsistency , Config ) ->
159
+ erase_inbox (Config ),
160
+ rpc (mim (), meck , unload , []),
161
+ escalus :end_per_testcase (Case , Config );
150
162
end_per_testcase (test_incremental_upsert , Config ) ->
151
163
erase_inbox (Config ),
152
164
escalus :end_per_testcase (test_incremental_upsert , Config );
165
+ end_per_testcase (Case = test_wrapped_request , Config ) ->
166
+ Tag = ? config (tag , Config ),
167
+ ok = rpc (mim (), mongoose_instrument , tear_down , [wrapper_event_spec (Tag )]),
168
+ escalus :end_per_testcase (Case , Config );
153
169
end_per_testcase (CaseName , Config ) ->
154
170
escalus :end_per_testcase (CaseName , Config ).
155
171
156
- declared_events (tagged_rdbms_queries ) ->
157
- instrument_helper :declared_events (mongoose_wpool_rdbms , [host_type (), tag ()]) ++
158
- [{test_wrapped_request , #{pool_tag => tag ()}}];
159
- declared_events (global_rdbms_queries ) ->
160
- instrument_helper :declared_events (mongoose_wpool_rdbms , [global , default ]) ++
161
- [{test_wrapped_request , #{pool_tag => global }}].
172
+ declared_events (Config ) ->
173
+ Scope = ? config (scope , Config ),
174
+ Tag = ? config (tag , Config ),
175
+ instrument_helper :declared_events (mongoose_wpool_rdbms , [Scope , Tag ]) ++
176
+ [{test_wrapped_request , #{pool_tag => Tag }}].
162
177
163
178
% %--------------------------------------------------------------------
164
179
% % Data for cases
@@ -369,11 +384,11 @@ read_prep_boolean_case(Config) ->
369
384
370
385
select_current_timestamp_case (Config ) ->
371
386
ok = rpc (mim (), mongoose_rdbms_timestamp , prepare , []),
372
- Res = case ? config (tag , Config ) of
373
- global ->
387
+ Res = case { ? config (scope , Config ), ? config ( tag , Config )} of
388
+ { global , default } ->
374
389
rpc (mim (), mongoose_rdbms_timestamp , select , []);
375
- Tag ->
376
- rpc (mim (), mongoose_rdbms_timestamp , select , [host_type () , Tag ])
390
+ { Scope , Tag } ->
391
+ rpc (mim (), mongoose_rdbms_timestamp , select , [Scope , Tag ])
377
392
end ,
378
393
assert_is_integer (Res ).
379
394
@@ -540,6 +555,31 @@ test_restart_transaction_with_execute_eventually_passes(Config) ->
540
555
called_times (3 ),
541
556
ok .
542
557
558
+ prepare_without_execute_does_not_cause_inconsistency (Config ) ->
559
+ prepare_insert_int8 (Config ),
560
+ Args = [rdbms , ? config (scope , Config ), ? config (tag , Config )],
561
+ Pool = rpc (mim (), mongoose_wpool , make_pool_name , Args ),
562
+ [Key1 , Key2 ] = make_hash_keys (2 , Pool ),
563
+
564
+ % % Simulate a call to 'prepare' with missing subsequent 'execute'
565
+ ok = rpc (mim (), meck , new , [mongoose_rdbms_backend , [passthrough , no_link ]]),
566
+ ok = rpc (mim (), meck , expect , [mongoose_rdbms_backend , execute , 4 , ok ]),
567
+ ok = call_worker (Args , Key1 , {sql_execute , insert_int8 , [1 ]}),
568
+
569
+ % % Insert the data using a different worker
570
+ Insert = <<" INSERT INTO inbox VALUES ('alice', 'localhost', 'bob@localhost', "
571
+ " 'msg-id', 'inbox', 'content', 14, 0, 0)" >>,
572
+ {updated , 1 } = call_worker (Args , Key2 , {sql_query , Insert }),
573
+
574
+ % % Make sure that worker 1 is not stuck in the previous transaction after 'prepare'
575
+ {selected , [{<<" 14" >>}]} = call_worker (Args , Key1 , {sql_query , <<" SELECT timestamp FROM inbox" >>}).
576
+
577
+ % % Send the SQL command to a particular DB worker
578
+ call_worker (Args , HashKey , Operation ) ->
579
+ TS = rpc (mim (), erlang , monotonic_time , [millisecond ]),
580
+ Command = {sql_cmd , Operation , TS },
581
+ rpc (mim (), mongoose_wpool , call , Args ++ [HashKey , Command ]).
582
+
543
583
test_failed_transaction_with_execute_wrapped (Config ) ->
544
584
% given
545
585
HostType = host_type (),
@@ -687,11 +727,11 @@ test_upsert_many2_replaces_existing(Config) ->
687
727
688
728
pool_probe_metrics_are_updated (Config ) ->
689
729
Tag = ? config (tag , Config ),
690
- {Event , Labels } = case Tag of
730
+ {Event , Labels } = case ? config ( scope , Config ) of
691
731
global ->
692
- {wpool_global_rdbms_stats , #{pool_tag => default }};
693
- Tag ->
694
- {wpool_rdbms_stats , #{host_type => host_type () , pool_tag => Tag }}
732
+ {wpool_global_rdbms_stats , #{pool_tag => Tag }};
733
+ Scope ->
734
+ {wpool_rdbms_stats , #{host_type => Scope , pool_tag => Tag }}
695
735
end ,
696
736
#{recv_oct := Recv , send_oct := Send } = rpc (mim (), mongoose_wpool_rdbms , probe , [Event , Labels ]),
697
737
@@ -718,10 +758,12 @@ tag() ->
718
758
extra_tag .
719
759
720
760
scope_and_tag (Config ) ->
721
- case ? config (tag , Config ) of
722
- global -> [host_type ()];
723
- Tag -> [host_type (), Tag ]
724
- end .
761
+ skip_default_tag ([? config (scope , Config ), ? config (tag , Config )]).
762
+
763
+ skip_default_tag ([Scope , default ]) ->
764
+ [Scope ];
765
+ skip_default_tag (ScopeAndTag ) ->
766
+ ScopeAndTag .
725
767
726
768
sql_query (Config , Query ) ->
727
769
ScopeAndTag = scope_and_tag (Config ),
@@ -1285,7 +1327,7 @@ stop_global_default_pool() ->
1285
1327
[GlobalRdbmsPool ] = [Pool || Pool = #{type := rdbms , scope := global , tag := default } <- Pools ],
1286
1328
ok = rpc (mim (), mongoose_wpool , stop , [rdbms , global , default ]),
1287
1329
Extra = maybe_stop_service_domain_db (),
1288
- [{tag , tag ()}, { global_default_rdbms_pool , GlobalRdbmsPool } | Extra ].
1330
+ [{global_default_rdbms_pool , GlobalRdbmsPool } | Extra ].
1289
1331
1290
1332
restart_global_default_pool (Config ) ->
1291
1333
GlobalRdbmsPool = ? config (global_default_rdbms_pool , Config ),
@@ -1390,3 +1432,24 @@ check_like_not_matching_prep(SelName, Config, _TextValue, NotMatching, Info) ->
1390
1432
SelectResult ,
1391
1433
Info #{pattern => NotMatching ,
1392
1434
select_result => SelectResult }).
1435
+
1436
+ % % Generate a list of Num numerical keys resolving to different Pool workers using hash_worker
1437
+ make_hash_keys (Num , Pool ) ->
1438
+ Workers = rpc (mim (), wpool_pool , get_workers , [Pool ]),
1439
+ if Num =< length (Workers ) ->
1440
+ lists :reverse (make_hash_keys (Num , Pool , Workers , 1 , []));
1441
+ true ->
1442
+ ct :fail (" Not enough workers in ~p (needed: ~p , actual: ~p )" ,
1443
+ [Pool , Num , length (Workers )])
1444
+ end .
1445
+
1446
+ make_hash_keys (RemainingNum , Pool , Workers , Key , Acc ) when RemainingNum > 0 ->
1447
+ Worker = rpc (mim (), wpool_pool , hash_worker , [Pool , Key ]),
1448
+ case lists :member (Worker , Workers ) of
1449
+ true ->
1450
+ make_hash_keys (RemainingNum - 1 , Pool , Workers -- [Worker ], Key + 1 , [Key | Acc ]);
1451
+ false ->
1452
+ make_hash_keys (RemainingNum , Pool , Workers , Key + 1 , Acc )
1453
+ end ;
1454
+ make_hash_keys (0 , _WorkerNum , _Workers , _Key , Acc ) ->
1455
+ Acc .
0 commit comments