341 lines
9.8 KiB
C++
341 lines
9.8 KiB
C++
/**
|
|
* Copyright (c) 2021 OceanBase
|
|
* OceanBase CE is licensed under Mulan PubL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PubL v2.
|
|
* You may obtain a copy of Mulan PubL v2 at:
|
|
* http://license.coscl.org.cn/MulanPubL-2.0
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PubL v2 for more details.
|
|
*/
|
|
|
|
#ifndef OCEANBASE_CLOG_OB_LOG_EXT_RING_BUFFER_H_
|
|
#define OCEANBASE_CLOG_OB_LOG_EXT_RING_BUFFER_H_
|
|
|
|
#include "share/ob_define.h"
|
|
#include "lib/container/ob_ext_ring_buffer.h"
|
|
#include "lib/time/ob_time_utility.h"
|
|
#include "common/ob_clock_generator.h"
|
|
|
|
namespace oceanbase {
|
|
namespace clog {
|
|
class ObILogExtRingBufferData : public common::ObExternalRef::IERefPtr {
|
|
public:
|
|
virtual bool can_overwrite(const ObILogExtRingBufferData* data) = 0;
|
|
|
|
virtual bool can_be_removed() = 0;
|
|
|
|
void reset()
|
|
{}
|
|
void on_quiescent()
|
|
{
|
|
destroy();
|
|
}
|
|
virtual void destroy() = 0;
|
|
};
|
|
|
|
class ObILogTaskCallBack {
|
|
public:
|
|
ObILogTaskCallBack()
|
|
{}
|
|
virtual ~ObILogTaskCallBack()
|
|
{}
|
|
|
|
public:
|
|
virtual int sliding_cb(const int64_t sn, const ObILogExtRingBufferData* data) = 0;
|
|
};
|
|
|
|
struct ObLogExtRingBufferPopCond {
|
|
ObLogExtRingBufferPopCond(ObILogTaskCallBack* task_cb)
|
|
: ret_(common::OB_SUCCESS), task_cb_(task_cb), is_replay_failed_(false)
|
|
{}
|
|
// Pop condition.
|
|
// NULL can't be popped, and pop() should return OB_EAGAIN.
|
|
// if can_be_removed() returns false, pop() should return OB_EAGAIN.
|
|
// on_pop() is called before data been popped.
|
|
bool operator()(const int64_t sn, ObILogExtRingBufferData* data)
|
|
{
|
|
bool ret = false;
|
|
ret_ = common::OB_SUCCESS;
|
|
if (NULL == data) {
|
|
// NULL can't be popped, return false, err code is OB_EAGAIN.
|
|
ret = false;
|
|
ret_ = common::OB_EAGAIN;
|
|
} else if (!data->can_be_removed()) {
|
|
// can_be_removed() returns false, return false, err code is OB_EAGAIN.
|
|
ret = false;
|
|
ret_ = common::OB_EAGAIN;
|
|
} else if (common::OB_SUCCESS != (ret_ = task_cb_->sliding_cb(sn, data))) {
|
|
// on_pop() err, return false, err code is on_pop()'s return code.
|
|
ret = false;
|
|
is_replay_failed_ = true;
|
|
} else {
|
|
// can pop, err code is OB_SUCCESS.
|
|
ret = true;
|
|
}
|
|
return ret;
|
|
}
|
|
int ret_;
|
|
ObILogTaskCallBack* task_cb_;
|
|
bool is_replay_failed_;
|
|
};
|
|
|
|
struct ObLogExtRingBufferTruncateCond {
|
|
// Pop everything.
|
|
bool operator()(const int64_t sn, ObILogExtRingBufferData* data)
|
|
{
|
|
UNUSED(sn);
|
|
UNUSED(data);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
struct ObLogExtRingBufferSetCond {
|
|
ObLogExtRingBufferSetCond() : olddata_(NULL)
|
|
{}
|
|
// Set condition. NULL can always be overwritten. olddata_ records the replaced ptr.
|
|
bool operator()(ObILogExtRingBufferData* olddata, ObILogExtRingBufferData* newdata)
|
|
{
|
|
// Old and new data can be NULL.
|
|
bool ret = false;
|
|
olddata_ = olddata;
|
|
if ((NULL == olddata) || (NULL != olddata && olddata->can_overwrite(newdata))) {
|
|
ret = true;
|
|
}
|
|
return ret;
|
|
}
|
|
ObILogExtRingBufferData* olddata_;
|
|
};
|
|
|
|
class ObLogExtRingBuffer : protected common::ObExtendibleRingBuffer<ObILogExtRingBufferData> {
|
|
typedef ObLogExtRingBuffer MyType;
|
|
typedef common::ObExtendibleRingBuffer<ObILogExtRingBufferData> BaseType;
|
|
|
|
public:
|
|
ObLogExtRingBuffer();
|
|
virtual ~ObLogExtRingBuffer();
|
|
inline int init(const int64_t start_id);
|
|
int destroy();
|
|
|
|
public:
|
|
int get(const int64_t id, ObILogExtRingBufferData*& val, const int64_t*& ref) const;
|
|
int revert(const int64_t* ref);
|
|
int set(const int64_t id, ObILogExtRingBufferData* data);
|
|
// -- TODO -- block seems unnecessary.
|
|
int pop(const bool block, const int64_t timeout, bool& is_replay_failed, ObILogTaskCallBack* task_cb);
|
|
/*
|
|
* Truncate ringbuffer & set new start id.
|
|
* Notice:
|
|
* - not thread safe.
|
|
* Error:
|
|
* - ...
|
|
*/
|
|
int truncate(const int64_t new_start_id);
|
|
int64_t get_start_id() const;
|
|
|
|
private:
|
|
int truncate_(const int64_t new_start_id);
|
|
|
|
private:
|
|
bool inited_;
|
|
DISALLOW_COPY_AND_ASSIGN(ObLogExtRingBuffer);
|
|
};
|
|
|
|
// Definitions.
|
|
|
|
inline ObLogExtRingBuffer::ObLogExtRingBuffer() : BaseType(), inited_(false)
|
|
{}
|
|
|
|
inline ObLogExtRingBuffer::~ObLogExtRingBuffer()
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
inline int ObLogExtRingBuffer::init(const int64_t start_id)
|
|
{
|
|
int ret = common::OB_SUCCESS;
|
|
if (inited_) {
|
|
ret = common::OB_INIT_TWICE;
|
|
CLOG_LOG(WARN, "double init", K(ret));
|
|
} else if (common::OB_SUCCESS != (ret = BaseType::init(start_id))) {
|
|
CLOG_LOG(WARN, "failed to init base", K(ret), K(start_id));
|
|
} else {
|
|
inited_ = true;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
inline int ObLogExtRingBuffer::destroy()
|
|
{
|
|
int ret = common::OB_SUCCESS;
|
|
if (!inited_) {
|
|
CLOG_LOG(INFO, "ObLogExtRingBuffer::destroy not inited");
|
|
} else if (common::OB_SUCCESS != (ret = truncate(end_sn()))) {
|
|
CLOG_LOG(WARN, "failed to truncate", K(ret));
|
|
} else if (common::OB_SUCCESS != (ret = BaseType::destroy())) {
|
|
CLOG_LOG(WARN, "failed to destroy base", K(ret));
|
|
} else {
|
|
inited_ = false;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
inline int ObLogExtRingBuffer::get(const int64_t id, ObILogExtRingBufferData*& val, const int64_t*& ref) const
|
|
{
|
|
int ret = common::OB_SUCCESS;
|
|
if (common::OB_SUCCESS != (ret = BaseType::get(id, val))) {
|
|
// Donnot print WARN log.
|
|
// CLOG_LOG(WARN, "failed to get data", K(ret), K(id));
|
|
}
|
|
ref = (int64_t*)val;
|
|
// It's in expectation that user may get() something out of upper bound.
|
|
// So convert this error code to ob_success.
|
|
if (common::OB_ERR_OUT_OF_UPPER_BOUND == ret) {
|
|
ret = common::OB_SUCCESS;
|
|
} else if (common::OB_ERR_OUT_OF_LOWER_BOUND == ret) {
|
|
ret = common::OB_ERROR_OUT_OF_RANGE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
inline int ObLogExtRingBuffer::revert(const int64_t* ref)
|
|
{
|
|
int ret = common::OB_SUCCESS;
|
|
if (!inited_) {
|
|
ret = common::OB_NOT_INIT;
|
|
CLOG_LOG(WARN, "not init", K(ret));
|
|
} else if (NULL == ref) {
|
|
// pass
|
|
} else {
|
|
get_val_ref().release((common::ObExternalRef::IERefPtr*)ref, get_retire_list());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
inline int ObLogExtRingBuffer::set(const int64_t id, ObILogExtRingBufferData* data)
|
|
{
|
|
int ret = common::OB_SUCCESS;
|
|
ObLogExtRingBufferSetCond cond;
|
|
bool set = false;
|
|
if (!inited_) {
|
|
ret = common::OB_NOT_INIT;
|
|
CLOG_LOG(WARN, "not init", K(ret));
|
|
} else if (NULL == data) {
|
|
ret = common::OB_INVALID_ARGUMENT;
|
|
CLOG_LOG(WARN, "invalid args", K(ret), K(id), K(data));
|
|
} else if (common::OB_SUCCESS != (ret = BaseType::set(id, data, cond, set))) {
|
|
CLOG_LOG(WARN, "failed to set data", K(ret), K(id), K(data), K(set));
|
|
} else if (!set) {
|
|
// cond returns false;
|
|
ret = common::OB_EAGAIN;
|
|
} else if (NULL != cond.olddata_) {
|
|
get_val_ref().retire(cond.olddata_, get_retire_list());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
inline int ObLogExtRingBuffer::pop(
|
|
const bool block, const int64_t timeout, bool& is_replay_failed, ObILogTaskCallBack* task_cb)
|
|
{
|
|
UNUSED(block);
|
|
int ret = common::OB_SUCCESS;
|
|
ObLogExtRingBufferPopCond cond(task_cb);
|
|
if (!inited_) {
|
|
ret = common::OB_NOT_INIT;
|
|
CLOG_LOG(WARN, "not init", K(ret));
|
|
} else {
|
|
int64_t begin_time = common::ObClockGenerator::getClock();
|
|
while (OB_SUCC(ret)) {
|
|
bool popped = false;
|
|
ObILogExtRingBufferData* data = NULL;
|
|
ret = BaseType::pop(cond, data, popped);
|
|
if (common::OB_ENTRY_NOT_EXIST == ret) {
|
|
// Empty queue.
|
|
ret = common::OB_EAGAIN;
|
|
} else if (OB_FAIL(ret)) {
|
|
// Error.
|
|
CLOG_LOG(WARN, "failed to pop", K(ret), K(data), K(popped));
|
|
} else {
|
|
if (!popped) {
|
|
// Not popped, use cond's ret.
|
|
ret = cond.ret_;
|
|
} else {
|
|
get_val_ref().retire(data, get_retire_list());
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && (timeout != 0) && ((common::ObClockGenerator::getClock() - begin_time) > timeout)) {
|
|
ret = common::OB_CLOG_SLIDE_TIMEOUT;
|
|
break;
|
|
}
|
|
}
|
|
if (common::OB_EAGAIN == ret) {
|
|
ret = common::OB_SUCCESS;
|
|
is_replay_failed = cond.is_replay_failed_;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
inline int ObLogExtRingBuffer::truncate_(const int64_t new_start_id)
|
|
{
|
|
int ret = common::OB_SUCCESS;
|
|
|
|
// Param check.
|
|
if (new_start_id < begin_sn()) {
|
|
ret = common::OB_INVALID_ARGUMENT;
|
|
CLOG_LOG(WARN, "invalid args", K(ret), K(new_start_id), "begin_sn", begin_sn());
|
|
}
|
|
|
|
// Prepare ringbuffer if new start id is greater than the last log id.
|
|
if (common::OB_SUCCESS == ret && end_sn() < new_start_id) {
|
|
// Set a NULL ptr at [new_start_id - 1], so new_start_id == end_sn().
|
|
// Ptrs before end_sn() are set to NULL automatically.
|
|
if (common::OB_SUCCESS != (ret = BaseType::set(new_start_id - 1, NULL))) {
|
|
CLOG_LOG(WARN, "failed to set data", K(ret), K(new_start_id));
|
|
}
|
|
}
|
|
|
|
// Keep popping till new_start_id.
|
|
ObLogExtRingBufferTruncateCond cond;
|
|
while (common::OB_SUCCESS == ret && begin_sn() < new_start_id) {
|
|
bool popped = false;
|
|
ObILogExtRingBufferData* data = NULL;
|
|
ret = BaseType::pop(cond, data, popped);
|
|
if (common::OB_SUCCESS != ret || !popped) {
|
|
// Should never happened.
|
|
CLOG_LOG(WARN, "failed to truncate", K(ret), K(new_start_id), "begin_sn", begin_sn());
|
|
ret = common::OB_ERR_UNEXPECTED;
|
|
} else if (NULL != data) {
|
|
get_val_ref().retire(data);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
inline int ObLogExtRingBuffer::truncate(const int64_t new_start_id)
|
|
{
|
|
int ret = common::OB_SUCCESS;
|
|
if (new_start_id < end_sn()) {
|
|
ret = truncate_(new_start_id);
|
|
} else {
|
|
if (OB_FAIL(truncate_(end_sn()))) {
|
|
CLOG_LOG(WARN, "failed to clear sliding window", K(ret));
|
|
} else {
|
|
set_range(new_start_id, new_start_id);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
inline int64_t ObLogExtRingBuffer::get_start_id() const
|
|
{
|
|
return BaseType::begin_sn();
|
|
}
|
|
|
|
} // namespace clog
|
|
} // namespace oceanbase
|
|
|
|
#endif
|