mirror of
https://github.com/scylladb/scylladb.git
synced 2026-04-26 11:30:36 +00:00
401 lines
12 KiB
C++
401 lines
12 KiB
C++
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* Modified by Cloudius Systems.
|
|
* Copyright 2015 Cloudius Systems.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#include "gms/inet_address.hh"
|
|
#include "core/shared_ptr.hh"
|
|
#include "core/thread.hh"
|
|
#include "core/distributed.hh"
|
|
#include "utils/class_registrator.hh"
|
|
|
|
namespace locator {
|
|
|
|
struct snitch_ptr;
|
|
|
|
typedef gms::inet_address inet_address;
|
|
|
|
struct i_endpoint_snitch {
|
|
private:
|
|
template <typename... A>
|
|
static future<> init_snitch_obj(
|
|
distributed<snitch_ptr>& snitch_obj, const sstring& snitch_name, A&&... a);
|
|
public:
|
|
template <typename... A>
|
|
static future<> create_snitch(const sstring& snitch_name, A&&... a);
|
|
|
|
template <typename... A>
|
|
static future<> reset_snitch(const sstring& snitch_name, A&&... a);
|
|
|
|
static future<> stop_snitch();
|
|
|
|
/**
|
|
* returns a String representing the rack this endpoint belongs to
|
|
*/
|
|
virtual sstring get_rack(inet_address endpoint) = 0;
|
|
|
|
/**
|
|
* returns a String representing the datacenter this endpoint belongs to
|
|
*/
|
|
virtual sstring get_datacenter(inet_address endpoint) = 0;
|
|
|
|
/**
|
|
* returns a new <tt>List</tt> sorted by proximity to the given endpoint
|
|
*/
|
|
virtual std::vector<inet_address> get_sorted_list_by_proximity(
|
|
inet_address address,
|
|
std::unordered_set<inet_address>& unsorted_address) = 0;
|
|
|
|
/**
|
|
* This method will sort the <tt>List</tt> by proximity to the given
|
|
* address.
|
|
*/
|
|
virtual void sort_by_proximity(
|
|
inet_address address, std::vector<inet_address>& addresses) = 0;
|
|
|
|
/**
|
|
* compares two endpoints in relation to the target endpoint, returning as
|
|
* Comparator.compare would
|
|
*/
|
|
virtual int compare_endpoints(
|
|
inet_address& target, inet_address& a1, inet_address& a2) = 0;
|
|
|
|
/**
|
|
* called after Gossiper instance exists immediately before it starts
|
|
* gossiping
|
|
*/
|
|
virtual void gossiper_starting() = 0;
|
|
|
|
/**
|
|
* Returns whether for a range query doing a query against merged is likely
|
|
* to be faster than 2 sequential queries, one against l1 followed by one
|
|
* against l2.
|
|
*/
|
|
virtual bool is_worth_merging_for_range_query(
|
|
std::vector<inet_address>& merged,
|
|
std::vector<inet_address>& l1,
|
|
std::vector<inet_address>& l2) = 0;
|
|
|
|
virtual ~i_endpoint_snitch() { assert(_state == snitch_state::stopped); };
|
|
|
|
virtual future<> stop() = 0;
|
|
|
|
// noop by default
|
|
virtual future<> pause_io() {
|
|
_state = snitch_state::io_paused;
|
|
return make_ready_future<>();
|
|
};
|
|
|
|
// noop by default
|
|
virtual void resume_io() {
|
|
_state = snitch_state::running;
|
|
};
|
|
|
|
// noop by default
|
|
virtual future<> start() {
|
|
_state = snitch_state::running;
|
|
return make_ready_future<>();
|
|
}
|
|
|
|
// noop by default
|
|
virtual void set_my_dc(const sstring& new_dc) {};
|
|
virtual void set_my_rack(const sstring& new_rack) {};
|
|
|
|
static distributed<snitch_ptr>& snitch_instance() {
|
|
static distributed<snitch_ptr> snitch_inst;
|
|
|
|
return snitch_inst;
|
|
}
|
|
|
|
static snitch_ptr& get_local_snitch_ptr() {
|
|
return snitch_instance().local();
|
|
}
|
|
|
|
void set_snitch_ready() {
|
|
_state = snitch_state::running;
|
|
}
|
|
|
|
virtual sstring get_name() const = 0;
|
|
|
|
// should be called for production snitches before calling start()
|
|
virtual void set_my_distributed(distributed<snitch_ptr>* d) {
|
|
//noop by default
|
|
}
|
|
|
|
protected:
|
|
static unsigned& io_cpu_id() {
|
|
static unsigned id = 0;
|
|
return id;
|
|
}
|
|
|
|
protected:
|
|
static logging::logger snitch_logger;
|
|
|
|
enum class snitch_state {
|
|
initializing,
|
|
running,
|
|
io_pausing,
|
|
io_paused,
|
|
stopping,
|
|
stopped
|
|
} _state = snitch_state::initializing;
|
|
};
|
|
|
|
struct snitch_ptr {
|
|
typedef std::unique_ptr<i_endpoint_snitch> ptr_type;
|
|
future<> stop() {
|
|
if (_ptr) {
|
|
return _ptr->stop();
|
|
} else {
|
|
return make_ready_future<>();
|
|
}
|
|
}
|
|
|
|
future<> start() {
|
|
if (_ptr) {
|
|
return _ptr->start();
|
|
} else {
|
|
return make_ready_future<>();
|
|
}
|
|
}
|
|
|
|
i_endpoint_snitch* operator->() {
|
|
return _ptr.get();
|
|
}
|
|
|
|
snitch_ptr& operator=(ptr_type&& new_val) {
|
|
_ptr = std::move(new_val);
|
|
|
|
return *this;
|
|
}
|
|
|
|
snitch_ptr& operator=(snitch_ptr&& new_val) {
|
|
_ptr = std::move(new_val._ptr);
|
|
|
|
return *this;
|
|
}
|
|
|
|
operator bool() const {
|
|
return _ptr ? true : false;
|
|
}
|
|
|
|
private:
|
|
ptr_type _ptr;
|
|
};
|
|
|
|
/**
|
|
* Initializes the distributed<snitch_ptr> object
|
|
*
|
|
* @note The local snitch objects will remain not start()ed.
|
|
*
|
|
* @param snitch_obj distributed<> object to initialize
|
|
* @param snitch_name name of the snitch class to create
|
|
* @param a snitch constructor arguments
|
|
*
|
|
* @return ready future when the snitch has been successfully created
|
|
*/
|
|
template <typename... A>
|
|
future<> i_endpoint_snitch::init_snitch_obj(
|
|
distributed<snitch_ptr>& snitch_obj, const sstring& snitch_name, A&&... a) {
|
|
|
|
// First, create the snitch_ptr objects...
|
|
return snitch_obj.start().then(
|
|
[&snitch_obj, snitch_name = std::move(snitch_name), a = std::make_tuple(std::forward<A>(a)...)] () {
|
|
// ...then, create the snitches...
|
|
return snitch_obj.invoke_on_all(
|
|
[snitch_name, a, &snitch_obj] (snitch_ptr& local_inst) {
|
|
try {
|
|
auto s(std::move(apply([snitch_name] (A&&... a) {
|
|
return create_object<i_endpoint_snitch>(snitch_name, std::forward<A>(a)...);
|
|
}, std::move(a))));
|
|
|
|
s->set_my_distributed(&snitch_obj);
|
|
local_inst = std::move(s);
|
|
} catch (no_such_class& e) {
|
|
snitch_logger.error("Can't create snitch {}: not supported", snitch_name);
|
|
throw;
|
|
} catch (...) {
|
|
throw;
|
|
}
|
|
|
|
return make_ready_future<>();
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* Creates the distributed i_endpoint_snitch::snitch_instane object
|
|
*
|
|
* @param snitch_name name of the snitch class (comes from the cassandra.yaml)
|
|
*
|
|
* @return ready future when the distributed object is ready.
|
|
*/
|
|
template <typename... A>
|
|
future<> i_endpoint_snitch::create_snitch(
|
|
const sstring& snitch_name, A&&... a) {
|
|
|
|
// First, create and "start" the distributed snitch object...
|
|
return init_snitch_obj(snitch_instance(), snitch_name, std::forward<A>(a)...).then([] {
|
|
// ...and then start each local snitch.
|
|
return snitch_instance().invoke_on_all([] (snitch_ptr& local_inst) {
|
|
return local_inst.start();
|
|
}).then_wrapped([] (auto&& f) {
|
|
try {
|
|
f.get();
|
|
return make_ready_future<>();
|
|
} catch (...) {
|
|
auto eptr = std::current_exception();
|
|
|
|
return stop_snitch().then([eptr] () {
|
|
std::rethrow_exception(eptr);
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Resets the global snitch instance with the new value
|
|
*
|
|
* @param snitch_name Name of a new snitch
|
|
* @param A optional parameters for a new snitch constructor
|
|
*
|
|
* @return ready future when the transition is complete
|
|
*
|
|
* The flow goes as follows:
|
|
* 1) Create a new distributed<snitch_ptr> and initialize it with the new
|
|
* snitch.
|
|
* 2) Start the new snitches above - this will initialize the snitches objects
|
|
* and will make them ready to be used.
|
|
* 3) Stop() the current global per-shard snitch objects.
|
|
* 4) Pause the per-shard snitch objects from (1) - this will stop the async
|
|
* I/O parts of the snitches if any.
|
|
* 5) Assign the per-shard snitch_ptr's from new distributed from (1) to the
|
|
* global one and update the distributed<> pointer in the new snitch
|
|
* instances.
|
|
* 6) Start the new snitches.
|
|
* 7) Stop() the temporary distributed<snitch_ptr> from (1).
|
|
*/
|
|
template <typename... A>
|
|
future<> i_endpoint_snitch::reset_snitch(
|
|
const sstring& snitch_name, A&&... a) {
|
|
return seastar::async(
|
|
[snitch_name, a = std::make_tuple(std::forward<A>(a)...)] {
|
|
|
|
// (1) create a new snitch
|
|
distributed<snitch_ptr> tmp_snitch;
|
|
try {
|
|
apply([snitch_name,&tmp_snitch](A&& ... a) {
|
|
return init_snitch_obj(tmp_snitch, snitch_name, std::forward<A>(a)...);
|
|
}, std::move(a)).get();
|
|
|
|
// (2) start the local instances of the new snitch
|
|
tmp_snitch.invoke_on_all([] (snitch_ptr& local_inst) {
|
|
return local_inst.start();
|
|
}).get();
|
|
} catch (...) {
|
|
tmp_snitch.stop().get();
|
|
throw;
|
|
}
|
|
|
|
// If we've got here then we may not fail
|
|
|
|
// (3) stop the current snitch instances on all CPUs
|
|
snitch_instance().invoke_on(io_cpu_id(), [] (snitch_ptr& s) {
|
|
// stop the instance on an I/O CPU first
|
|
return s->stop();
|
|
}).get();
|
|
snitch_instance().invoke_on_all([] (snitch_ptr& s) {
|
|
return s->stop();
|
|
}).get();
|
|
|
|
//
|
|
// (4) If we've got here - the new snitch has been successfully created
|
|
// and initialized. We may pause its I/O it now and start moving
|
|
// pointers...
|
|
//
|
|
tmp_snitch.invoke_on(io_cpu_id(), [] (snitch_ptr& local_inst) {
|
|
// pause the instance on an I/O CPU first
|
|
return local_inst->pause_io();
|
|
}).get();
|
|
tmp_snitch.invoke_on_all([] (snitch_ptr& local_inst) {
|
|
return local_inst->pause_io();
|
|
}).get();
|
|
|
|
//
|
|
// (5) move the pointers - this would ensure the atomicity on a
|
|
// per-shard level (since users are holding snitch_ptr objects only)
|
|
//
|
|
tmp_snitch.invoke_on_all([] (snitch_ptr& local_inst) {
|
|
local_inst->set_my_distributed(&snitch_instance());
|
|
snitch_instance().local() = std::move(local_inst);
|
|
|
|
return make_ready_future<>();
|
|
}).get();
|
|
|
|
// (6) re-start I/O on the new snitches
|
|
snitch_instance().invoke_on_all([] (snitch_ptr& local_inst) {
|
|
local_inst->resume_io();
|
|
}).get();
|
|
|
|
// (7) stop the temporary from (1)
|
|
tmp_snitch.stop().get();
|
|
});
|
|
}
|
|
|
|
class snitch_base : public i_endpoint_snitch {
|
|
public:
|
|
//
|
|
// Sons have to implement:
|
|
// virtual sstring get_rack(inet_address endpoint) = 0;
|
|
// virtual sstring get_datacenter(inet_address endpoint) = 0;
|
|
//
|
|
|
|
virtual std::vector<inet_address> get_sorted_list_by_proximity(
|
|
inet_address address,
|
|
std::unordered_set<inet_address>& unsorted_address) override;
|
|
|
|
virtual void sort_by_proximity(
|
|
inet_address address, std::vector<inet_address>& addresses) override;
|
|
|
|
virtual int compare_endpoints(
|
|
inet_address& address, inet_address& a1, inet_address& a2) override;
|
|
|
|
// noop by default
|
|
virtual void gossiper_starting() override {}
|
|
|
|
virtual bool is_worth_merging_for_range_query(
|
|
std::vector<inet_address>& merged,
|
|
std::vector<inet_address>& l1,
|
|
std::vector<inet_address>& l2) override;
|
|
|
|
private:
|
|
bool has_remote_node(std::vector<inet_address>& l);
|
|
|
|
protected:
|
|
sstring _my_dc;
|
|
sstring _my_rack;
|
|
};
|
|
|
|
} // namespace locator
|