diff --git a/core/config/common_provider/CommonConfigProvider.cpp b/core/config/common_provider/CommonConfigProvider.cpp index 571a6feb85..de4c985f54 100644 --- a/core/config/common_provider/CommonConfigProvider.cpp +++ b/core/config/common_provider/CommonConfigProvider.cpp @@ -261,6 +261,10 @@ void CommonConfigProvider::GetConfigUpdate() { LOG_DEBUG(sLogger, ("fetch instanceConfig config, config file number", instanceConfig.size())); UpdateRemoteInstanceConfig(instanceConfig); } + const auto& onetimeCommands = heartbeatResponse.onetime_pipeline_config_updates(); + if (!onetimeCommands.empty()) { + UpdateRemoteOnetimePipelineConfig(onetimeCommands); + } ++mSequenceNum; } @@ -270,7 +274,8 @@ configserver::proto::v2::HeartbeatRequest CommonConfigProvider::PrepareHeartbeat heartbeatReq.set_request_id(requestID); heartbeatReq.set_sequence_num(mSequenceNum); heartbeatReq.set_capabilities(configserver::proto::v2::AcceptsInstanceConfig - | configserver::proto::v2::AcceptsContinuousPipelineConfig); + | configserver::proto::v2::AcceptsContinuousPipelineConfig + | configserver::proto::v2::AcceptsOnetimePipelineConfig); heartbeatReq.set_instance_id(GetInstanceId()); heartbeatReq.set_agent_type("LoongCollector"); FillAttributes(*heartbeatReq.mutable_attributes()); @@ -494,6 +499,61 @@ void CommonConfigProvider::UpdateRemoteInstanceConfig( } } +void CommonConfigProvider::UpdateRemoteOnetimePipelineConfig( + const google::protobuf::RepeatedPtrField& commands) { + int64_t now = static_cast(time(nullptr)); + for (const auto& cmd : commands) { + if (cmd.expire_time() > 0 && cmd.expire_time() < now) { + continue; + } + // Check under mInfoMapMux alone — do NOT hold mOnetimePipelineMux here to avoid + // lock-order inversion with the file-I/O section below. + { + lock_guard lockInfoMap(mInfoMapMux); + if (mOnetimePipelineConfigInfoMap.count(cmd.name())) { + continue; + } + } + filesystem::path filePath = mOnetimePipelineConfigDir / (cmd.name() + ".json"); + filesystem::path tmpFilePath = mOnetimePipelineConfigDir / (cmd.name() + ".json.new"); + // File I/O under mOnetimePipelineMux alone — never hold mInfoMapMux simultaneously + // to maintain a strict single-level lock hierarchy and eliminate deadlock risk. + { + lock_guard lock(mOnetimePipelineMux); + { + ofstream fout(tmpFilePath); + if (!fout) { + LOG_WARNING(sLogger, ("failed to open onetime config file", tmpFilePath.string())); + continue; + } + fout << cmd.detail(); + } + error_code ec; + // Remove the target first so that filesystem::rename succeeds on Windows, + // where rename fails with an error if the destination already exists. + filesystem::remove(filePath, ec); + filesystem::rename(tmpFilePath, filePath, ec); + if (ec) { + LOG_WARNING(sLogger, + ("failed to rename onetime config file", filePath.string())("error code", ec.value())( + "error msg", ec.message())); + filesystem::remove(tmpFilePath, ec); + continue; + } + } + { + lock_guard lockInfoMap(mInfoMapMux); + ConfigInfo info; + info.name = cmd.name(); + info.version = cmd.expire_time() > 0 ? cmd.expire_time() : 1; + info.status = ConfigFeedbackStatus::APPLYING; + mOnetimePipelineConfigInfoMap[cmd.name()] = std::move(info); + } + ConfigFeedbackReceiver::GetInstance().RegisterOnetimePipelineConfig(cmd.name(), this); + LOG_INFO(sLogger, ("received onetime pipeline config", cmd.name())("expire_time", cmd.expire_time())); + } +} + bool CommonConfigProvider::FetchInstanceConfigFromServer( ::configserver::proto::v2::HeartbeatResponse& heartbeatResponse, ::google::protobuf::RepeatedPtrField< ::configserver::proto::v2::ConfigDetail>& res) { diff --git a/core/config/common_provider/CommonConfigProvider.h b/core/config/common_provider/CommonConfigProvider.h index 59aa1a6727..8ba99c3818 100644 --- a/core/config/common_provider/CommonConfigProvider.h +++ b/core/config/common_provider/CommonConfigProvider.h @@ -70,6 +70,8 @@ class CommonConfigProvider : public ConfigProvider, ConfigFeedbackable { const google::protobuf::RepeatedPtrField& configs); void UpdateRemoteInstanceConfig( const google::protobuf::RepeatedPtrField& configs); + void UpdateRemoteOnetimePipelineConfig( + const google::protobuf::RepeatedPtrField& commands); virtual bool FetchInstanceConfigFromServer(::configserver::proto::v2::HeartbeatResponse&,