Skip to content
Snippets Groups Projects
Commit 974e69c2 authored by Georgy Moshkin's avatar Georgy Moshkin :speech_balloon:
Browse files

feat: .proc_instance_info

parent 4c97b961
No related branches found
No related tags found
1 merge request!840Info proc API
use crate::instance::Grade;
use crate::instance::InstanceId;
use crate::replicaset::ReplicasetId;
use crate::traft::error::Error;
use crate::traft::node;
use crate::traft::RaftId;
use std::borrow::Cow;
use tarantool::proc;
......@@ -41,3 +47,96 @@ impl VersionInfo<'static> {
pub fn proc_version_info() -> VersionInfo<'static> {
VersionInfo::current()
}
////////////////////////////////////////////////////////////////////////////////
// InstanceInfo
////////////////////////////////////////////////////////////////////////////////
/// Info returned from [`.proc_instance_info`].
///
/// [`.proc_instance_info`]: proc_instance_info
#[derive(Clone, Debug, ::serde::Serialize, ::serde::Deserialize)]
pub struct InstanceInfo {
pub raft_id: RaftId,
pub advertise_address: String,
pub instance_id: InstanceId,
pub instance_uuid: String,
pub replicaset_id: ReplicasetId,
pub replicaset_uuid: String,
pub cluster_id: String,
pub current_grade: Grade,
pub target_grade: Grade,
pub tier: String,
}
impl tarantool::tuple::Encode for InstanceInfo {}
impl InstanceInfo {
pub fn try_get(node: &node::Node, instance_id: Option<&InstanceId>) -> Result<Self, Error> {
let instance;
match instance_id {
None => {
let instance_id = node.raft_storage.instance_id()?;
let instance_id =
instance_id.expect("should be persisted before Node is initialized");
instance = node.storage.instances.get(&instance_id)?;
}
Some(instance_id) => {
instance = node.storage.instances.get(instance_id)?;
}
}
let peer_address = node
.storage
.peer_addresses
.get(instance.raft_id)?
.unwrap_or_else(|| "<unknown>".into());
let cluster_id = node.raft_storage.cluster_id()?;
Ok(InstanceInfo {
raft_id: instance.raft_id,
advertise_address: peer_address,
instance_id: instance.instance_id,
instance_uuid: instance.instance_uuid,
replicaset_id: instance.replicaset_id,
replicaset_uuid: instance.replicaset_uuid,
cluster_id,
current_grade: instance.current_grade,
target_grade: instance.target_grade,
tier: instance.tier,
})
}
}
////////////////////////////////////////////////////////////////////////////////
// .proc_instance_info
////////////////////////////////////////////////////////////////////////////////
#[proc(packed_args)]
pub fn proc_instance_info(request: InstanceInfoRequest) -> Result<InstanceInfo, Error> {
let node = node::global()?;
let instance_id = match &request {
InstanceInfoRequest::CurrentInstance(_) => None,
InstanceInfoRequest::ByInstanceId([instance_id]) => Some(instance_id),
};
InstanceInfo::try_get(node, instance_id)
}
#[derive(Debug, ::serde::Deserialize, ::serde::Serialize)]
#[serde(untagged)]
enum InstanceInfoRequest {
// FIXME: this is the simplest way I found to support a single optional
// parameter to the stored procedure. We should probably do something about
// it in our custom `Encode`/`Decode` traits.
CurrentInstance([(); 0]),
ByInstanceId([InstanceId; 1]),
}
impl ::tarantool::tuple::Encode for InstanceInfoRequest {}
impl crate::rpc::RequestArgs for InstanceInfoRequest {
const PROC_NAME: &'static str = crate::stringify_cfunc!(proc_instance_info);
type Response = InstanceInfo;
}
......@@ -2,6 +2,7 @@
#![allow(clippy::too_many_arguments)]
#![allow(clippy::let_and_return)]
#![allow(clippy::needless_return)]
#![allow(clippy::needless_late_init)]
#![allow(clippy::unwrap_or_default)]
#![allow(clippy::redundant_static_lifetimes)]
use serde::{Deserialize, Serialize};
......
......@@ -152,13 +152,13 @@ pub(crate) fn setup(args: &args::Run) {
"},
tlua::function0(|| -> traft::Result<_> {
let node = traft::node::global()?;
let raft_storage = &node.raft_storage;
let info = crate::info::InstanceInfo::try_get(node, None)?;
Ok(tlua::AsTable((
("raft_id", raft_storage.raft_id()?),
("cluster_id", raft_storage.cluster_id()?),
("instance_id", raft_storage.instance_id()?),
("tier", raft_storage.tier()?),
("raft_id", info.raft_id),
("cluster_id", info.cluster_id),
("instance_id", info.instance_id),
("tier", info.tier),
)))
}),
);
......@@ -219,24 +219,18 @@ pub(crate) fn setup(args: &args::Run) {
"},
tlua::function1(|iid: Option<InstanceId>| -> traft::Result<_> {
let node = traft::node::global()?;
let iid = iid.unwrap_or(node.raft_storage.instance_id()?.unwrap());
let instance = node.storage.instances.get(&iid)?;
let peer_address = node
.storage
.peer_addresses
.get(instance.raft_id)?
.unwrap_or_else(|| "<unknown>".into());
let info = crate::info::InstanceInfo::try_get(node, iid.as_ref())?;
Ok(tlua::AsTable((
("raft_id", instance.raft_id),
("advertise_address", peer_address),
("instance_id", instance.instance_id.0),
("instance_uuid", instance.instance_uuid),
("replicaset_id", instance.replicaset_id),
("replicaset_uuid", instance.replicaset_uuid),
("current_grade", instance.current_grade),
("target_grade", instance.target_grade),
("tier", instance.tier),
("raft_id", info.raft_id),
("advertise_address", info.advertise_address),
("instance_id", info.instance_id.0),
("instance_uuid", info.instance_uuid),
("replicaset_id", info.replicaset_id),
("replicaset_uuid", info.replicaset_uuid),
("current_grade", info.current_grade),
("target_grade", info.target_grade),
("tier", info.tier),
)))
}),
);
......
......@@ -94,6 +94,10 @@ impl RaftSpaceAccess {
Ok(res)
}
/// Returns the persisted `InstanceId` of the current instance.
/// This should be persisted before the global [`Node`] is initialized.
///
/// [`Node`]: crate::traft::node::Node
#[inline(always)]
pub fn instance_id(&self) -> tarantool::Result<Option<InstanceId>> {
let res = self.try_get_raft_state("instance_id")?;
......
......@@ -325,3 +325,51 @@ def test_governor_notices_restarts(instance: Instance):
def test_proc_version_info(instance: Instance):
info = instance.call(".proc_version_info")
assert info.keys() == set(["picodata_version", "proc_api_version"]) # type: ignore
def test_proc_instance_info(cluster: Cluster):
cfg = {
"tier": {
"storage": {"replication_factor": 1},
"router": {"replication_factor": 2},
}
}
cluster.set_init_cfg(cfg)
i1 = cluster.add_instance(tier="storage")
i2 = cluster.add_instance(tier="router")
i1_info = i1.call(".proc_instance_info")
assert i1_info == dict(
raft_id=1,
advertise_address=f"{i1.host}:{i1.port}",
instance_id="i1",
instance_uuid=i1.instance_uuid(),
replicaset_id="r1",
replicaset_uuid=i1.replicaset_uuid(),
cluster_id=i1.cluster_id,
current_grade=dict(variant="Online", incarnation=1),
target_grade=dict(variant="Online", incarnation=1),
tier="storage",
)
info = i1.call(".proc_instance_info", "i1")
assert i1_info == info
i2_info = i1.call(".proc_instance_info", "i2")
assert i2_info == dict(
raft_id=2,
advertise_address=f"{i2.host}:{i2.port}",
instance_id="i2",
instance_uuid=i2.instance_uuid(),
replicaset_id="r2",
replicaset_uuid=i2.replicaset_uuid(),
cluster_id=i1.cluster_id,
current_grade=dict(variant="Online", incarnation=1),
target_grade=dict(variant="Online", incarnation=1),
tier="router",
)
with pytest.raises(TarantoolError) as e:
i1.call(".proc_instance_info", "i3")
assert 'instance with id "i3" not found' in str(e)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment