blob: ba9581003fedab1e479387bc597199ad867f15a7 [file]
/*
* 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.
*/
#include "LocalFileOffsetStore.h"
#include <fstream>
#include "Logging.h"
#include "MQClientInstance.h"
#include "MessageQueue.hpp"
#include "UtilAll.h"
namespace rocketmq {
LocalFileOffsetStore::LocalFileOffsetStore(MQClientInstance* instance, const std::string& groupName)
: client_instance_(instance), group_name_(groupName) {
LOG_INFO("new LocalFileOffsetStore");
std::string clientId = instance->getClientId();
std::string homeDir(UtilAll::getHomeDirectory());
std::string storeDir =
homeDir + FILE_SEPARATOR + ".rocketmq_offsets" + FILE_SEPARATOR + clientId + FILE_SEPARATOR + groupName;
store_path_ = storeDir + FILE_SEPARATOR + "offsets.json";
if (!UtilAll::existDirectory(storeDir)) {
UtilAll::createDirectory(storeDir);
if (!UtilAll::existDirectory(storeDir)) {
LOG_ERROR_NEW("create offset store directory failed: {}", storeDir);
std::string errorMsg("create offset store directory failed: ");
errorMsg.append(storeDir);
THROW_MQEXCEPTION(MQClientException, errorMsg, -1);
}
}
}
LocalFileOffsetStore::~LocalFileOffsetStore() {
client_instance_ = nullptr;
offset_table_.clear();
}
void LocalFileOffsetStore::load() {
auto offsetTable = readLocalOffset();
if (!offsetTable.empty()) {
// update offsetTable
{
std::lock_guard<std::mutex> lock(lock_);
offset_table_ = offsetTable;
}
for (const auto& it : offsetTable) {
const auto& mq = it.first;
const auto offset = it.second;
LOG_INFO_NEW("load consumer's offset, {} {} {}", group_name_, mq.toString(), offset);
}
}
}
void LocalFileOffsetStore::updateOffset(const MQMessageQueue& mq, int64_t offset, bool increaseOnly) {
std::lock_guard<std::mutex> lock(lock_);
offset_table_[mq] = offset;
}
int64_t LocalFileOffsetStore::readOffset(const MQMessageQueue& mq, ReadOffsetType type) {
switch (type) {
case MEMORY_FIRST_THEN_STORE:
case READ_FROM_MEMORY: {
std::lock_guard<std::mutex> lock(lock_);
const auto& it = offset_table_.find(mq);
if (it != offset_table_.end()) {
return it->second;
} else if (READ_FROM_MEMORY == type) {
return -1;
}
} break;
case READ_FROM_STORE: {
auto offsetTable = readLocalOffset();
if (!offsetTable.empty()) {
const auto& it = offsetTable.find(mq);
if (it != offsetTable.end()) {
auto offset = it->second;
updateOffset(mq, offset, false);
return offset;
}
}
} break;
default:
break;
}
LOG_ERROR("can not readOffset from offsetStore.json, maybe first time consumation");
return -1;
}
void LocalFileOffsetStore::persist(const MQMessageQueue& mq) {}
void LocalFileOffsetStore::persistAll(std::vector<MQMessageQueue>& mqs) {
if (mqs.empty()) {
return;
}
std::map<MQMessageQueue, int64_t> offsetTable;
{
std::lock_guard<std::mutex> lock(lock_);
offsetTable = offset_table_;
}
Json::Value root(Json::objectValue);
Json::Value jOffsetTable(Json::objectValue);
for (const auto& mq : mqs) {
const auto& it = offsetTable.find(mq);
if (it != offsetTable.end()) {
std::string strMQ = RemotingSerializable::toJson(toJson(mq));
jOffsetTable[strMQ] = Json::Value((Json::Int64)it->second);
}
}
root["offsetTable"] = jOffsetTable;
std::lock_guard<std::mutex> lock2(file_mutex_);
std::string storePathTmp = store_path_ + ".tmp";
std::ofstream ofstrm(storePathTmp, std::ios::binary | std::ios::out);
if (ofstrm.is_open()) {
try {
RemotingSerializable::toJson(root, ofstrm, true);
} catch (std::exception& e) {
THROW_MQEXCEPTION(MQClientException, "persistAll failed", -1);
}
if (!UtilAll::ReplaceFile(store_path_, store_path_ + ".bak") || !UtilAll::ReplaceFile(storePathTmp, store_path_)) {
LOG_ERROR("could not rename file: %s", strerror(errno));
}
}
}
void LocalFileOffsetStore::removeOffset(const MQMessageQueue& mq) {}
std::map<MQMessageQueue, int64_t> LocalFileOffsetStore::readLocalOffset() {
std::lock_guard<std::mutex> lock(file_mutex_);
std::ifstream ifstrm(store_path_, std::ios::binary | std::ios::in);
if (ifstrm.is_open() && !ifstrm.eof()) {
try {
Json::Value root = RemotingSerializable::fromJson(ifstrm);
std::map<MQMessageQueue, int64_t> offsetTable;
auto& jOffsetTable = root["offsetTable"];
for (auto& strMQ : jOffsetTable.getMemberNames()) {
auto& offset = jOffsetTable[strMQ];
Json::Value jMQ = RemotingSerializable::fromJson(strMQ);
MQMessageQueue mq(jMQ["topic"].asString(), jMQ["brokerName"].asString(), jMQ["queueId"].asInt());
offsetTable.emplace(std::move(mq), offset.asInt64());
}
return offsetTable;
} catch (std::exception& e) {
// ...
}
}
return readLocalOffsetBak();
}
std::map<MQMessageQueue, int64_t> LocalFileOffsetStore::readLocalOffsetBak() {
std::map<MQMessageQueue, int64_t> offsetTable;
std::ifstream ifstrm(store_path_ + ".bak", std::ios::binary | std::ios::in);
if (ifstrm.is_open()) {
if (!ifstrm.eof()) {
try {
Json::Value root = RemotingSerializable::fromJson(ifstrm);
auto& jOffsetTable = root["offsetTable"];
for (auto& strMQ : jOffsetTable.getMemberNames()) {
auto& offset = jOffsetTable[strMQ];
Json::Value jMQ = RemotingSerializable::fromJson(strMQ);
MQMessageQueue mq(jMQ["topic"].asString(), jMQ["brokerName"].asString(), jMQ["queueId"].asInt());
offsetTable.emplace(std::move(mq), offset.asInt64());
}
} catch (const std::exception& e) {
LOG_WARN_NEW("readLocalOffset Exception {}", e.what());
THROW_MQEXCEPTION(MQClientException, "readLocalOffset Exception", -1);
}
}
}
return offsetTable;
}
} // namespace rocketmq