/** * 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. */ #ifndef __STATE_STATE_HPP__ #define __STATE_STATE_HPP__ #include #include #include #include #include #include #include #include "logging/logging.hpp" #include "messages/state.hpp" #include "state/serializer.hpp" namespace mesos { namespace internal { namespace state { // An abstraction of "state" (possibly between multiple distributed // components) represented by "variables" (effectively key/value // pairs). Variables are versioned such that setting a variable in the // state will only succeed if the variable has not changed since last // fetched. Varying implementations of state provide varying // replicated guarantees. // // Note that the semantics of 'get' and 'set' provide atomicity. That // is, you can not set a variable that has changed since you did the // last get. That is, if a set succeeds then no other writes have been // performed on the variable since your get. // Example: // State* state = new ZooKeeperState(); // Future > variable = state->get("slaves"); // Variable slaves = variable.get(); // slaves->add_infos()->MergeFrom(info); // Future set = state->set(&slaves); // Forward declarations. template class State; class ZooKeeperStateProcess; template class Variable { public: T* operator -> () { return &t; } private: template friend class State; // Creates and manages variables. Variable(const Entry& _entry, const T& _t) : entry(_entry), t(_t) {} Entry entry; // Not const so Variable is copyable. T t; }; template class State { public: State() {} virtual ~State() {} // Returns a variable from the state, creating a new one if one // previously did not exist (or an error if one occurs). template process::Future > get(const std::string& name); // Returns true if the variable was successfully set in the state, // otherwise false if the version of the variable was no longer // valid (or an error if one occurs). template process::Future > > set(const Variable& variable); // Returns the collection of variable names in the state. virtual process::Future > names() = 0; protected: // Fetch and swap state entries, factored out to allow State // implementations to be agnostic of Variable which is templated. virtual process::Future > fetch(const std::string& name) = 0; virtual process::Future swap(const Entry& entry, const UUID& uuid) = 0; private: // Helpers to handle future results from fetch and swap. We make // these static members of State for friend access to Variable's // constructor. template static process::Future > _get( const std::string& name, const Option& option); template static process::Future > > _set( const Entry& entry, const T& t, const bool& b); // TODO(benh): Remove 'const &' after fixing libprocess. }; template template process::Future > State::_get( const std::string& name, const Option& option) { if (option.isSome()) { const Entry& entry = option.get(); Try t = Serializer::template deserialize(entry.value()); if (t.isError()) { return process::Future >::failed(t.error()); } return Variable(entry, t.get()); } // Otherwise, construct a Variable out of a new Entry with a default // value for T (and a random UUID to start). T t; Try value = Serializer::template serialize(t); if (value.isError()) { return process::Future >::failed(value.error()); } Entry entry; entry.set_name(name); entry.set_uuid(UUID::random().toBytes()); entry.set_value(value.get()); return Variable(entry, t); } template template process::Future > > State::_set( const Entry& entry, const T& t, const bool& b) // TODO(benh): Remove 'const &' after fixing libprocess. { if (b) { return Option >::some(Variable(entry, t)); } return None(); } template template process::Future > State::get(const std::string& name) { return fetch(name) .then(std::tr1::bind(&State::template _get, name, std::tr1::placeholders::_1)); } template template process::Future > > State::set( const Variable& variable) { Try value = Serializer::template serialize(variable.t); if (value.isError()) { return process::Future > >::failed(value.error()); } // Note that we try and swap an entry even if the value didn't change! UUID uuid = UUID::fromBytes(variable.entry.uuid()); // Create a new entry that should be replace the existing entry // provided the UUID matches. Entry entry; entry.set_name(variable.entry.name()); entry.set_uuid(UUID::random().toBytes()); entry.set_value(value.get()); std::tr1::function< process::Future > >(const bool&)> _set = std::tr1::bind(&State::template _set, entry, variable.t, std::tr1::placeholders::_1); return swap(entry, uuid).then(_set); } } // namespace state { } // namespace internal { } // namespace mesos { #endif // __STATE_STATE_HPP__