aboutsummaryrefslogtreecommitdiffstats
path: root/documentapi/src/test/java/com/yahoo/documentapi/messagebus/protocol/test/storagepolicy/BasicTests.java
blob: d84dc23df00560a2bd33cd1b9af824da102418c5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.documentapi.messagebus.protocol.test.storagepolicy;

import com.yahoo.collections.Pair;
import com.yahoo.documentapi.messagebus.protocol.WrongDistributionReply;
import com.yahoo.messagebus.Error;
import com.yahoo.messagebus.ErrorCode;
import com.yahoo.messagebus.Reply;
import com.yahoo.messagebus.routing.RoutingNode;
import org.junit.Test;

import java.util.Arrays;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;

public class BasicTests extends ContentPolicyTestEnvironment {

    /** Test that we can send a message through the policy. */
    @Test
    public void testNormalUsage() {
        setClusterNodes(new int[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
        // First we want a wrong distribution reply, so make sure we don't try correct node on random
        policyFactory.avoidPickingAtRandom(bucketOneNodePreference[0]);
        RoutingNode target = select();
        replyWrongDistribution(target, "foo", 5, "version:1 bits:16 distributor:10 storage:10");
        // Then send to correct node and verify that
        sendToCorrectNode("foo", bucketOneNodePreference[0]);
    }

    /** Test that we can identify newest cluster state and hang on to correct one. */
    @Test
    public void testRepliesWrongOrderDuringStateChange() throws Exception{
        {
            setClusterNodes(new int[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
            RoutingNode target1 = select();
            RoutingNode target2 = select();
            replyWrongDistribution(target2, "foo", 0, "version:2 bits:16 distributor:10");
            replyWrongDistribution(target1, "foo", 5, "version:1 bits:16 distributor:10 ." + bucketOneNodePreference[0] + ".s:d");
            sendToCorrectNode("foo", bucketOneNodePreference[0]);
        }
        tearDown();
        setUp();
        {
            setClusterNodes(new int[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
            RoutingNode target1 = select();
            RoutingNode target2 = select();
            replyWrongDistribution(target2, "foo", 0, "version:1 bits:16 distributor:10");
            replyWrongDistribution(target1, "foo", 5, "version:2 bits:16 distributor:10 ." + bucketOneNodePreference[0] + ".s:d");
            sendToCorrectNode("foo", bucketOneNodePreference[1]);
        }
    }
    /**
     * To be independent of changes in distribution algorithm, we programmatically calculate preferred order of
     * bucket 1, which we will be using in the tests. To avoid doing this ahead of every test, we still hardcode the
     * values, though only one place, and have this test to verify they are correct, and make it easy to update the values.
     */
    @Test
    public void testVerifyBucketOneNodePreferenceInTenNodeDefaultCluster() {
        int result[] = new int[10];
        setClusterNodes(new int[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
        String clusterState = " bits:16 storage:10 distributor:10";
        for (int i=0; i<10; ++i) {
            // Update cached cluster state, to reflect which node we want to find
            RoutingNode target = select();
            target.handleReply(new WrongDistributionReply("version:" + (i + 1) + clusterState));
            Reply reply = frame.getReceptor().getReply(60);
            assertNotNull(reply);
            assertFalse(reply.hasErrors());
            // Find correct target
            target = select();
            Pair<String, Integer> address = getAddress(target);
            result[i] = address.getSecond();
            removeNode(address.getSecond());
            clusterState += " ." + result[i] + ".s:d";
        }
        assertEquals(Arrays.toString(bucketOneNodePreference), Arrays.toString(result));
    }

    private void letPolicyCacheStateWithNoAvailableDistributors() {
        setClusterNodes(new int[]{ 0 });
        String clusterState = " bits:16 storage:1"; // No distributors online
        var target = select();
        // Update to cluster state with no distributors online
        replyWrongDistribution(target, "foo", null, clusterState);
    }

    @Test
    public void random_node_target_used_if_no_distributors_available_in_cached_state() {
        letPolicyCacheStateWithNoAvailableDistributors();
        select(); // Will trigger failure if no target is set, i.e. if an exception is thrown
    }

    @Test
    public void policy_context_set_on_no_distributors_exception_during_select() {
        letPolicyCacheStateWithNoAvailableDistributors();
        // Re-select with no distributors. Should still get a target selection context.
        var target = select();
        // Trigger a reply merge with error, which will try to inspect the context.
        // If an exception is thrown internally from a missing context, the reply will not be
        // set correctly and this will fail.
        replyError(target, new Error(ErrorCode.FATAL_ERROR, "oh no!"));
    }

}