1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2013 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18#include "config.h"
19
20#include <signal.h>
21
22#include <algorithm>
23#include <vector>
24
25#include "configuration.h"
26#include "stats.h"
27#include "threadtests.h"
28#include "vbucket.h"
29#include "vbucketmap.h"
30
31static const size_t numThreads = 10;
32static const size_t vbucketsEach = 100;
33
34EPStats global_stats;
35CheckpointConfig checkpoint_config;
36
37extern "C" {
38    static rel_time_t basic_current_time(void) {
39        return 0;
40    }
41
42    rel_time_t (*ep_current_time)() = basic_current_time;
43
44    time_t ep_real_time() {
45        return time(NULL);
46    }
47}
48
49class VBucketGenerator {
50public:
51    VBucketGenerator(EPStats &s, CheckpointConfig &c, int start = 1)
52        : st(s), config(c), i(start) {}
53    VBucket *operator()() {
54        return new VBucket(i++, vbucket_state_active, st, config, NULL);
55    }
56private:
57    EPStats &st;
58    CheckpointConfig &config;
59    int i;
60};
61
62static void assertVBucket(const VBucketMap& vbm, int id) {
63    RCPtr<VBucket> v = vbm.getBucket(id);
64    cb_assert(v);
65    cb_assert(v->getId() == id);
66}
67
68static void testVBucketLookup() {
69    VBucketGenerator vbgen(global_stats, checkpoint_config);
70    std::vector<VBucket*> bucketList(3);
71    std::generate_n(bucketList.begin(), bucketList.capacity(), vbgen);
72
73    Configuration config;
74    VBucketMap vbm(config);
75    vbm.addBuckets(bucketList);
76
77    cb_assert(!vbm.getBucket(4));
78    assertVBucket(vbm, 1);
79    assertVBucket(vbm, 2);
80    assertVBucket(vbm, 3);
81}
82
83class AtomicUpdater : public Generator<bool> {
84public:
85    AtomicUpdater(VBucketMap *m) : vbm(m), i(0) {}
86    bool operator()() {
87        for (size_t j = 0; j < vbucketsEach; j++) {
88            int newId = ++i;
89
90            RCPtr<VBucket> v(new VBucket(newId, vbucket_state_active,
91                                         global_stats, checkpoint_config, NULL));
92            vbm->addBucket(v);
93            cb_assert(vbm->getBucket(newId) == v);
94
95            if (newId % 2 == 0) {
96                vbm->removeBucket(newId);
97            }
98        }
99
100        return true;
101    }
102private:
103    VBucketMap *vbm;
104    Atomic<int> i;
105};
106
107static void testConcurrentUpdate(void) {
108    Configuration config;
109    config.setMaxNumShards((size_t)1);
110    VBucketMap vbm(config);
111    AtomicUpdater au(&vbm);
112    getCompletedThreads<bool>(numThreads, &au);
113
114    // We remove half of the buckets in the test
115    std::vector<int> rv;
116    vbm.getBuckets(rv);
117    cb_assert(rv.size() == ((numThreads * vbucketsEach) / 2));
118}
119
120static void testVBucketFilter() {
121    VBucketFilter empty;
122
123    cb_assert(empty(0));
124    cb_assert(empty(1));
125    cb_assert(empty(2));
126
127    std::vector<uint16_t> v;
128
129    VBucketFilter emptyTwo(v);
130    cb_assert(emptyTwo(0));
131    cb_assert(emptyTwo(1));
132    cb_assert(emptyTwo(2));
133
134    v.push_back(2);
135
136    VBucketFilter hasOne(v);
137    cb_assert(!hasOne(0));
138    cb_assert(!hasOne(1));
139    cb_assert(hasOne(2));
140
141    v.push_back(0);
142
143    VBucketFilter hasTwo(v);
144    cb_assert(hasTwo(0));
145    cb_assert(!hasTwo(1));
146    cb_assert(hasTwo(2));
147
148    v.push_back(1);
149
150    VBucketFilter hasThree(v);
151    cb_assert(hasThree(0));
152    cb_assert(hasThree(1));
153    cb_assert(hasThree(2));
154    cb_assert(!hasThree(3));
155}
156
157static void assertFilterTxt(const VBucketFilter &filter, const std::string &res)
158{
159    std::stringstream ss;
160    ss << filter;
161    if (ss.str() != res) {
162        std::cerr << "Filter mismatch: "
163                  << ss.str() << " != " << res << std::endl;
164        std::cerr.flush();
165        abort();
166    }
167}
168
169static void testVBucketFilterFormatter(void) {
170    std::set<uint16_t> v;
171
172    VBucketFilter filter(v);
173    assertFilterTxt(filter, "{ empty }");
174    v.insert(1);
175    filter.assign(v);
176    assertFilterTxt(filter, "{ 1 }");
177
178    for (uint16_t ii = 2; ii < 100; ++ii) {
179        v.insert(ii);
180    }
181    filter.assign(v);
182    assertFilterTxt(filter, "{ [1,99] }");
183
184    v.insert(101);
185    v.insert(102);
186    filter.assign(v);
187    assertFilterTxt(filter, "{ [1,99], 101, 102 }");
188
189    v.insert(103);
190    filter.assign(v);
191    assertFilterTxt(filter, "{ [1,99], [101,103] }");
192
193    v.insert(100);
194    filter.assign(v);
195    assertFilterTxt(filter, "{ [1,103] }");
196}
197
198static void testGetVBucketsByState(void) {
199    Configuration config;
200    VBucketMap vbm(config);
201
202    int st = vbucket_state_dead;
203    for (int id = 0; id < 4; id++, st--) {
204        RCPtr<VBucket> v(new VBucket(id, (vbucket_state_t)st, global_stats,
205                                     checkpoint_config, NULL));
206        vbm.addBucket(v);
207        cb_assert(vbm.getBucket(id) == v);
208    }
209}
210
211int main(int argc, char **argv) {
212    (void)argc; (void)argv;
213    putenv(strdup("ALLOW_NO_STATS_UPDATE=yeah"));
214
215    HashTable::setDefaultNumBuckets(5);
216    HashTable::setDefaultNumLocks(1);
217
218    alarm(60);
219
220    testVBucketLookup();
221    testConcurrentUpdate();
222    testVBucketFilter();
223    testVBucketFilterFormatter();
224    testGetVBucketsByState();
225}
226