Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions docs/source/deployment/ssd/nvmf-ssd-deployment-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,37 @@ python3 -m mooncake.mooncake_ssd_register \
| `--password` | SSH password used to connect to target nodes. |
| `--key-file` | SSH private key file used to connect to target nodes. |

### 4.2 Register without Target SPDK RPC

Use `--nqn` and `--traddr` instead of `--spdk_target_info` to register an existing
namespace. The script asks Master to query namespace information over NVMe-oF
and register its full range as a NoF segment, without target-side SSH or SPDK
management RPC:

```bash
MC_NOF_TRTYPE=TCP python3 -m mooncake.mooncake_ssd_register \
--master_server_address=192.168.65.81:50051 \
--nqn=nqn.2016-06.io.spdk:cnode1 \
--traddr=192.168.65.56 \
--trsvcid=4420 \
--nsid=1
```

#### Parameters

| Parameter | Description |
|-----------|-------------|
| `--master_server_address` | Required. IP address and port of the Master service. |
| `--nqn` | Required. NQN of the target subsystem. |
| `--traddr` | Required target IPv4 address. |
| `--trsvcid` | NVMe-oF service port. Defaults to `4420`. |
| `--nsid` | Namespace ID within the subsystem. Defaults to `1`. |

Master requires a `USE_NOF=ON` build, SPDK setup, and access to the target.
The registration node only needs access to Master. Set `MC_NOF_TRTYPE` on that
node to `RDMA` (default) or `TCP`.


## 5. Unregister the NVMe-oF SSD Pool

### 5.1 Unregister a Specific SSD
Expand Down
8 changes: 8 additions & 0 deletions mooncake-integration/store/store_py.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2201,6 +2201,14 @@ PYBIND11_MODULE(store, m) {
self.register_ = std::make_shared<NoFRegisterClient>();
return self.register_->set_unregister_by_endpoint(
nqn, nsid, traddr, trsvcid, master_server_addr);
})
.def("query_and_register",
[](MooncakeDistributedNoFRegisterPyWrapper &self,
const std::string &nqn, size_t nsid, const std::string &traddr,
size_t trsvcid, const std::string &master_server_addr) {
self.register_ = std::make_shared<NoFRegisterClient>();
return self.register_->query_and_register(
nqn, nsid, traddr, trsvcid, master_server_addr);
});
// Create a wrapper that exposes DistributedObjectStore with Python-specific
// methods
Expand Down
9 changes: 9 additions & 0 deletions mooncake-store/include/master_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,15 @@ class MasterClient {
[[nodiscard]] tl::expected<void, ErrorCode> MountNoFSegment(
const NoFSegment& segment);

/**
* @brief Ask the master to query NoF namespace information and
* register its full range for allocation.
* @param endpoint NVMe-oF transport string.
* @return tl::expected<void, ErrorCode> indicating success/failure.
*/
[[nodiscard]] tl::expected<void, ErrorCode> QueryAndMountNoFSegment(
const std::string& endpoint);

/**
* @brief Re-mount segments, invoked when the client is the first time to
* connect to the master or the client Ping TTL is expired and need
Expand Down
21 changes: 21 additions & 0 deletions mooncake-store/include/master_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ class MasterService {
public:
using NoFProbeFn =
std::function<bool(const std::string&, uint32_t, std::string*)>;
using NoFNamespaceQueryFn = std::function<bool(
const std::string&, NoFNamespaceInfo&, std::string*)>;
using DurableFinalizeCallback =
std::function<void(const OpLogEntry& durable_entry)>;
using BatchOpLogWriterFactory =
Expand All @@ -189,6 +191,7 @@ class MasterService {
~MasterService();

void SetNoFProbeFnForTesting(NoFProbeFn fn);
void SetNoFNamespaceQueryFnForTesting(NoFNamespaceQueryFn fn);
size_t GetMountedNoFSegmentCountForTesting();
bool IsNoFSegmentMountedForTesting(const UUID& segment_id);
std::optional<uint32_t> GetNoFHeartbeatFailureCountForTesting(
Expand Down Expand Up @@ -266,6 +269,22 @@ class MasterService {
auto MountNoFSegment(const NoFSegment& segment, const UUID& client_id)
-> tl::expected<void, ErrorCode>;

/**
* @brief Query NoF namespace information over NVMe-oF and mount its full
* range for buffer allocation, with base=0.
* A mounted endpoint whose full range was already verified succeeds without
* querying again. Legacy mounts are queried and checked before becoming
* eligible for this fast path. Unmounting discards the verification.
* @return Success or an error from MountNoFSegment,
* ErrorCode::INVALID_PARAMS if an existing endpoint has a different
* range,
* ErrorCode::UNAVAILABLE_IN_CURRENT_MODE if NoF is disabled,
* ErrorCode::INTERNAL_ERROR if the namespace query fails.
*/
auto QueryAndMountNoFSegment(const std::string& endpoint,
const UUID& client_id)
-> tl::expected<void, ErrorCode>;

/**
* @brief Re-mount segments, invoked when the client is the first time to
* connect to the master or the client Ping TTL is expired and need
Expand Down Expand Up @@ -2663,6 +2682,8 @@ class MasterService {
static constexpr uint64_t kNoFHeartbeatThreadSleepMs = 100;
mutable std::mutex nof_probe_fn_mutex_;
NoFProbeFn nof_probe_fn_;
std::mutex nof_namespace_query_fn_mutex_;
NoFNamespaceQueryFn nof_namespace_query_fn_;

// if high availability features enabled
const bool enable_ha_;
Expand Down
3 changes: 3 additions & 0 deletions mooncake-store/include/rpc_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ class WrappedMasterService {
tl::expected<void, ErrorCode> MountNoFSegment(const NoFSegment& segment,
const UUID& client_id);

tl::expected<void, ErrorCode> QueryAndMountNoFSegment(
const std::string& endpoint, const UUID& client_id);

tl::expected<void, ErrorCode> ReMountSegment(
const std::vector<Segment>& segments, const UUID& client_id);

Expand Down
6 changes: 6 additions & 0 deletions mooncake-store/include/spdk/spdk_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ constexpr int kSpdkNofOpNum = 2;
struct nof_seg_handle;
struct tr_info;
struct ctrlr_info;
struct NoFNamespaceInfo;

class SpdkWrapper {
public:
Expand Down Expand Up @@ -53,6 +54,11 @@ class SpdkWrapper {
bool ProbeNofSegment(const std::string &tr_str, uint32_t timeout_ms,
std::string *error_reason = nullptr);

// Query namespace capacity and block size, reusing a connected controller
// when available. Connections opened for this query are closed on return.
bool QueryNamespaceInfo(const std::string &endpoint, NoFNamespaceInfo &info,
std::string *error_reason = nullptr);

private:
struct ProbeBuffer {
void *ptr{nullptr};
Expand Down
13 changes: 13 additions & 0 deletions mooncake-store/include/ssd_register_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@ class NoFRegisterClient {
const std::string &traddr, size_t trsvcid,
const std::string &master_server_addr);

/**
* @brief Ask the master to query and register a complete NoF namespace.
* @param nqn Subsystem NQN.
* @param nsid Namespace ID.
* @param traddr Target transport address.
* @param trsvcid Target transport service ID.
* @param master_server_addr Master server address.
* @return int OPERATION_OK or OPERATION_FAILED
*/
int query_and_register(const std::string &nqn, size_t nsid,
const std::string &traddr, size_t trsvcid,
const std::string &master_server_addr);

private:
MasterClient master_client_;
};
Expand Down
6 changes: 6 additions & 0 deletions mooncake-store/include/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,12 @@ struct NoFSegment {
};
YLT_REFL(NoFSegment, id, name, base, size, te_endpoint);

struct NoFNamespaceInfo {
uint64_t size = 0;
uint64_t num_blocks = 0;
uint32_t block_size = 0;
};

/**
* @brief Client status from the master's perspective
*/
Expand Down
18 changes: 18 additions & 0 deletions mooncake-store/src/master_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ struct RpcNameTraits<&WrappedMasterService::MountNoFSegment> {
static constexpr const char* value = "MountNoFSegment";
};

template <>
struct RpcNameTraits<&WrappedMasterService::QueryAndMountNoFSegment> {
static constexpr const char* value = "QueryAndMountNoFSegment";
};

template <>
struct RpcNameTraits<&WrappedMasterService::ReMountSegment> {
static constexpr const char* value = "ReMountSegment";
Expand Down Expand Up @@ -858,6 +863,19 @@ tl::expected<void, ErrorCode> MasterClient::MountNoFSegment(
return result;
}

tl::expected<void, ErrorCode> MasterClient::QueryAndMountNoFSegment(
const std::string& endpoint) {
ScopedVLogTimer timer(1, "MasterClient::QueryAndMountNoFSegment");
timer.LogRequest("NoF segment mount: ", "endpoint=", endpoint,
", client_id=", client_id_);

auto result =
invoke_rpc<&WrappedMasterService::QueryAndMountNoFSegment, void>(
endpoint, client_id_);
timer.LogResponseExpected(result);
return result;
}

tl::expected<void, ErrorCode> MasterClient::ReMountSegment(
const std::vector<Segment>& segments) {
ScopedVLogTimer timer(1, "MasterClient::ReMountSegment");
Expand Down
90 changes: 90 additions & 0 deletions mooncake-store/src/master_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,12 @@ MasterService::MasterService(const MasterServiceConfig& config)
return SpdkWrapper::GetInstance().ProbeNofSegment(
te_endpoint, timeout_ms, error_reason);
};
nof_namespace_query_fn_ = [](const std::string& endpoint,
NoFNamespaceInfo& info,
std::string* error_reason) {
return SpdkWrapper::GetInstance().QueryNamespaceInfo(endpoint, info,
error_reason);
};
#endif

// Offload-on-evict: defer LOCAL_DISK offload to eviction time
Expand Down Expand Up @@ -817,6 +823,24 @@ void MasterService::SetNoFProbeFnForTesting(NoFProbeFn fn) {
#endif
}

void MasterService::SetNoFNamespaceQueryFnForTesting(NoFNamespaceQueryFn fn) {
#ifdef USE_NOF
std::lock_guard<std::mutex> lock(nof_namespace_query_fn_mutex_);
if (fn) {
nof_namespace_query_fn_ = std::move(fn);
return;
}
nof_namespace_query_fn_ = [](const std::string& endpoint,
NoFNamespaceInfo& info,
std::string* error_reason) {
return SpdkWrapper::GetInstance().QueryNamespaceInfo(endpoint, info,
error_reason);
};
#else
(void)fn;
#endif
}

size_t MasterService::GetMountedNoFSegmentCountForTesting() {
std::vector<MountedNoFSegmentSnapshot> mounted_segments;
nof_segment_manager_.GetMountedSegmentsSnapshot(mounted_segments);
Expand Down Expand Up @@ -1042,6 +1066,72 @@ auto MasterService::MountNoFSegment(const NoFSegment& segment,
#endif
}

auto MasterService::QueryAndMountNoFSegment(const std::string& endpoint,
const UUID& client_id)
-> tl::expected<void, ErrorCode> {
#ifndef USE_NOF
LOG(ERROR) << "client_id=" << client_id << ", segment_name=" << endpoint
<< ", error=nof_pool_disabled";
return tl::make_unexpected(ErrorCode::UNAVAILABLE_IN_CURRENT_MODE);
#else
NoFNamespaceQueryFn query_fn;
{
std::lock_guard<std::mutex> lock(nof_namespace_query_fn_mutex_);
query_fn = nof_namespace_query_fn_;
}
NoFNamespaceInfo info;
std::string error_reason;
if (!query_fn(endpoint, info, &error_reason)) {
LOG(ERROR) << "NoF namespace query failed: client_id=" << client_id
<< ", endpoint=" << endpoint << ", error=" << error_reason;
return tl::make_unexpected(ErrorCode::INTERNAL_ERROR);
}

NoFSegment segment;
segment.id = generate_uuid();
segment.name = endpoint;
segment.te_endpoint = endpoint;
segment.base = 0;
segment.size = info.size;

auto segment_access = nof_segment_manager_.getNoFSegmentAccess();
std::vector<MountedNoFSegmentSnapshot> mounted_segments;
auto err = segment_access.GetMountedSegments(mounted_segments);
if (err != ErrorCode::OK) {
return tl::make_unexpected(err);
}
bool already_mounted = false;
for (const auto& existing : mounted_segments) {
if (existing.segment.te_endpoint != endpoint) {
continue;
}
if (existing.status != SegmentStatus::OK) {
return tl::make_unexpected(
ErrorCode::UNAVAILABLE_IN_CURRENT_STATUS);
}
if (existing.segment.base != segment.base ||
existing.segment.size != segment.size) {
LOG(ERROR) << "NoF namespace range mismatch: client_id="
<< client_id << ", endpoint=" << endpoint
<< ", mounted_base=" << existing.segment.base
<< ", mounted_size=" << existing.segment.size
<< ", queried_base=" << segment.base
<< ", queried_size=" << segment.size;
return tl::make_unexpected(ErrorCode::INVALID_PARAMS);
}
already_mounted = true;
}
if (already_mounted) {
return {};
}
err = segment_access.MountSegment(segment, client_id);
if (err != ErrorCode::OK) {
return tl::make_unexpected(err);
}
return {};
#endif
}

ErrorCode MasterService::ValidateStandbyRemountSegment(
const Segment& segment) const {
const StandbySegmentInfo* match = nullptr;
Expand Down
22 changes: 22 additions & 0 deletions mooncake-store/src/rpc_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,25 @@ tl::expected<void, ErrorCode> WrappedMasterService::MountNoFSegment(
});
}

tl::expected<void, ErrorCode> WrappedMasterService::QueryAndMountNoFSegment(
const std::string& endpoint, const UUID& client_id) {
return execute_rpc(
"QueryAndMountNoFSegment",
[&] {
return master_service_.QueryAndMountNoFSegment(endpoint, client_id);
},
[&](auto& timer) {
timer.LogRequest("NoF segment mount: ", "endpoint=", endpoint,
", client_id=", client_id);
},
[] {
MasterMetricManager::instance().inc_mount_nof_segment_requests();
},
[] {
MasterMetricManager::instance().inc_mount_nof_segment_failures();
});
}

tl::expected<void, ErrorCode> WrappedMasterService::ReMountSegment(
const std::vector<Segment>& segments, const UUID& client_id) {
return execute_rpc(
Expand Down Expand Up @@ -1807,6 +1826,9 @@ void RegisterRpcService(
&wrapped_master_service);
server.register_handler<&mooncake::WrappedMasterService::MountNoFSegment>(
&wrapped_master_service);
server.register_handler<
&mooncake::WrappedMasterService::QueryAndMountNoFSegment>(
&wrapped_master_service);
server.register_handler<&mooncake::WrappedMasterService::ReMountSegment>(
&wrapped_master_service);
server.register_handler<&mooncake::WrappedMasterService::ReMountNoFSegment>(
Expand Down
Loading
Loading