1#!/bin/sh
2#
3# @author Couchbase <info@couchbase.com>
4# @copyright 2016-2018 Couchbase, Inc.
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#      http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18# A script that drops all replicas for a particular vbucket and then recreates
19# them from active copy. If vbucket was replicated using tap, then it will
20# also update the replication to dcp.
21#
22# Use as follows:
23#
24#   ./rebuild_replicas.sh <username> <password> <host:rest port> <bucket> <vbucket>
25#
26set -e
27
28user=$1
29password=$2
30host=$3
31bucket=$4
32vbucket=$5
33sleep=${6:-10000}
34
35curl --fail -X POST -u $user:$password http://$host/diag/eval -d @- <<EOF
36Bucket = "${bucket}",
37VBucket = ${vbucket},
38Sleep = ${sleep},
39
40GetChainsFromBucket =
41  fun () ->
42    {ok, Conf} = ns_bucket:get_bucket(Bucket),
43    {map, Map} = lists:keyfind(map, 1, Conf),
44    OldChain = lists:nth(VBucket+1, Map),
45    NewChain = [hd(OldChain)] ++ [undefined || _ <- tl(OldChain)],
46    {OldChain, NewChain}
47  end,
48
49WaitForRebalance =
50  fun (Rec) ->
51    case (catch ns_orchestrator:rebalance_progress_full()) of
52      not_running ->
53        ok;
54      _ ->
55        Rec(Rec)
56    end
57  end,
58
59SyncConfig =
60  fun () ->
61    Nodes = ns_node_disco:nodes_wanted(),
62
63    ns_config_rep:pull_and_push(Nodes),
64    ns_config:sync_announcements(),
65    ok = ns_config_rep:synchronize_remote(Nodes)
66  end,
67
68Rebalance =
69  fun (C) ->
70    error_logger:info_msg("Starting fixup rebalance ~p", [{VBucket, C}]),
71
72    SyncConfig(),
73    ok = ns_orchestrator:ensure_janitor_run({bucket, Bucket}),
74    ok = gen_fsm:sync_send_event({via, leader_registry, ns_orchestrator},
75                                 {move_vbuckets, Bucket, [{VBucket, C}]}),
76    error_logger:info_msg("Waiting for a fixup rebalance ~p to complete", [{VBucket, C}]),
77    WaitForRebalance(WaitForRebalance),
78    error_logger:info_msg("Fixup rebalance ~p is complete", [{VBucket, C}])
79  end,
80
81UpdateReplType =
82  fun () ->
83    SyncConfig(),
84
85    {ok, Conf} = ns_bucket:get_bucket(Bucket),
86    case ns_bucket:replication_type(Conf) of
87      dcp ->
88        ok;
89      Type ->
90        TapVBuckets =
91          case Type of
92            tap ->
93              lists:seq(0, proplists:get_value(num_vbuckets, Conf) - 1);
94            {dcp, Vs} ->
95              Vs
96          end,
97        NewType =
98          case ordsets:del_element(VBucket, TapVBuckets) of
99            [] ->
100              dcp;
101            NewTapVBuckets ->
102              {dcp, NewTapVBuckets}
103          end,
104        error_logger:info_msg("Updating replication type for bucket ~p:~n~p", [Bucket, NewType]),
105        ns_bucket:update_bucket_props(Bucket, [{repl_type, NewType}]),
106        ns_config:sync_announcements(),
107        ok = ns_config_rep:synchronize_remote([mb_master:master_node()])
108    end
109  end,
110
111{OldChain, NoReplicasChain} =
112  case ns_config:search({fixup_rebalance, Bucket, VBucket}) of
113    {value, Chains} ->
114      error_logger:info_msg("Found unfinished fixup rebalance for ~p. Chains:~n~p", [VBucket, Chains]),
115      Chains;
116    false ->
117      Chains = GetChainsFromBucket(),
118      ns_config:set({fixup_rebalance, Bucket, VBucket}, Chains),
119      Chains
120  end,
121
122Rebalance(NoReplicasChain),
123UpdateReplType(),
124Rebalance(OldChain),
125ns_config:delete({fixup_rebalance, Bucket, VBucket}),
126
127timer:sleep(Sleep).
128EOF
129