anchor.diff:       tester_join_at,
        add_9, rm_5, add_9_rm_5,
        add_2x3_load,
  -     add_3_rm_1_load,
  -     add_3_rm_2_load,
  -     add_3_rm_3_load,
        tester_join_at_timeouts
       ].
   
  
positive.code:     unittest_helper:create_ct_all([join_lookup]) ++
          unittest_helper:create_ct_all([add_3_rm_3_data]) ++
          unittest_helper:create_ct_all([add_3_rm_3_data_inc]) ++
          test_cases().
  
  groups() ->
  %%     unittest_helper:create_ct_groups(test_cases(), [{add_9_rm_5, [sequence, {repeat_until_any_fail, forever}]}]).
      unittest_helper:create_ct_groups([join_lookup], [{join_lookup, [sequence, {repeat_until_any_fail, 20}]}]) ++
      unittest_helper:create_ct_groups([add_3_rm_3_data], [{add_3_rm_3_data, [sequence, {repeat_until_any_fail, 20}]}]) ++
      unittest_helper:create_ct_groups([add_3_rm_3_data_inc], [{add_3_rm_3_data_inc, [sequence, {repeat_until_any_fail, 20}]}]).
  
  suite() -> [ {timetrap, {seconds, 30}} ].
  
  
negative.code:     ct:pal("######## waiting for bench finish ########"),
      util:wait_for_process_to_die(BenchPid).
  
  make_4_add_1_rm_1_load(Config) ->
      make_4_add_x_rm_y_load(Config, 1, 1, true).
  
  make_4_add_2_rm_2_load(Config) ->
      make_4_add_x_rm_y_load(Config, 2, 2, true).
  
  make_4_add_3_rm_3_load(Config) ->
      make_4_add_x_rm_y_load(Config, 3, 3, true).
  
  -spec make_4_add_x_rm_y_load(Config::[tuple()], X::non_neg_integer(), Y::pos_integer(),
                               StartOnlyAdded::boolean()) -> boolean().
  make_4_add_x_rm_y_load(Config, X, Y, StartOnlyAdded) ->
      {priv_dir, PrivDir} = lists:keyfind(priv_dir, 1, Config),
      unittest_helper:make_ring_with_ids(?RT:get_replica_keys(?MINUS_INFINITY), [{config, [{log_path, PrivDir}, {monitor_perf_interval, 0} | join_parameters_list()]}]),
      stop_time(fun() -> add_x_rm_y_load_test(X, Y, StartOnlyAdded) end, lists:flatten(io_lib:format("add_~B_rm_~B_load", [X, Y]))),
      unittest_helper:check_ring_load(40),
      unittest_helper:check_ring_data().
  
  -spec add_x_rm_y_load_test(X::non_neg_integer(), Y::pos_integer(), StartOnlyAdded::boolean()) -> ok.
  add_x_rm_y_load_test(X, Y, StartOnlyAdded) ->
      BenchPid = erlang:spawn(fun() -> bench:increment(10, 1000) end),
      {Started, []} = api_vm:add_nodes(X),
      check_size(X + 4),
      timer:sleep(500),
      Ring = [begin
                  Pred = hd(node_details:get(Details, predlist)),
                  Node = node_details:get(Details, node),
                  {node:id(Node), ?RT:get_range(node:id(Pred), node:id(Node)) / ?RT:n()}
              end || {ok, Details} <- statistics:get_ring_details()],
      ct:pal("RING: ~.2p", [Ring]),
      ct:pal("######## finish join ########"),
      % let Y nodes gracefully leave, one after another - not more than minority of tm_tms can die!
      ToShutdown =
          if StartOnlyAdded -> util:random_subset(Y, Started);
             true -> [pid_groups:group_of(Pid)|| Pid <- util:random_subset(Y, pid_groups:find_all(dht_node))]
          end,
      [begin
           Pid = pid_groups:pid_of(Group, dht_node),
           ct:pal("Killing #~p: ~p (~p)", [I, Pid, Group]),
           comm:send_local(Pid, {leave, null}),
           check_size(X + 4 - I)
       end || {I, Group} <- lists:zip(lists:seq(1, Y), ToShutdown)],
      check_size(X + 4 - Y),
      ct:pal("######## waiting for bench finish ########"),
      util:wait_for_process_to_die(BenchPid).
  
  