// 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 BRPC_CLUSTER_RECOVER_POLICY
#define BRPC_CLUSTER_RECOVER_POLICY

#include <cstdint>
#include <memory>
#include "butil/synchronization/lock.h"
#include "butil/strings/string_piece.h"
#include "butil/strings/string_number_conversions.h"

namespace brpc {

struct ServerId;

// After all servers are down and health check happens, servers are
// online one by one. Once one server is up, all the request that should
// be sent to all servers, would be sent to one server, which may be a
// disastrous behaviour. In the worst case it would cause the server being down
// again if circuit breaker is enabled and the cluster would never recover.
// This class controls the amount of requests that sent to the revived
// servers when recovering from all servers are down.
class ClusterRecoverPolicy {
public:
    virtual ~ClusterRecoverPolicy() {}

    // Indicate that recover from all server being down is happening.
    virtual void StartRecover() = 0;

    // Return true if some customized policies are satisfied.
    virtual bool DoReject(const std::vector<ServerId>& server_list) = 0;

    // Stop recover state and do not reject the request if some condition is
    // satisfied. Return true if the current state is still in recovering.
    virtual bool StopRecoverIfNecessary() = 0;
};

// The default cluster recover policy. Once no servers are available, recover is start.
// If in recover state, the probability that a request is accepted is q/n, in
// which q is the number of current available server, n is the number of minimum
// working instances setting by user. If q is not changed during a given time,
// hold_seconds, then the cluster is considered recovered and all the request
// would be sent to the current available servers.
class DefaultClusterRecoverPolicy : public ClusterRecoverPolicy {
public:
    DefaultClusterRecoverPolicy(int64_t min_working_instances, int64_t hold_seconds);

    void StartRecover() override;
    bool DoReject(const std::vector<ServerId>& server_list) override;
    bool StopRecoverIfNecessary() override;

private:
    uint64_t GetUsableServerCount(int64_t now_ms, const std::vector<ServerId>& server_list);

private:
    bool _recovering;
    int64_t _min_working_instances;
    butil::Mutex _mutex;
    uint64_t _last_usable;
    int64_t _last_usable_change_time_ms;
    int64_t _hold_seconds;
    uint64_t _usable_cache;
    int64_t _usable_cache_time_ms;
};

// Return a DefaultClusterRecoverPolicy object by params.
bool GetRecoverPolicyByParams(const butil::StringPiece& params,
                              std::shared_ptr<ClusterRecoverPolicy>* ptr_out);

} // namespace brpc

#endif

