Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement xlinectl compaction and member commands #484

Merged
merged 3 commits into from
Oct 18, 2023
Merged
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
6 changes: 4 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions xline-client/src/types/cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub use xlineapi::{
};

/// Request for `MemberAdd`
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub struct MemberAddRequest {
/// The inner request
inner: xlineapi::MemberAddRequest,
Expand All @@ -31,7 +31,7 @@ impl From<MemberAddRequest> for xlineapi::MemberAddRequest {
}

/// Request for `MemberList`
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub struct MemberListRequest {
/// The inner request
inner: xlineapi::MemberListRequest,
Expand All @@ -56,7 +56,7 @@ impl From<MemberListRequest> for xlineapi::MemberListRequest {
}

/// Request for `MemberPromote`
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub struct MemberPromoteRequest {
/// The inner request
inner: xlineapi::MemberPromoteRequest,
Expand All @@ -81,7 +81,7 @@ impl From<MemberPromoteRequest> for xlineapi::MemberPromoteRequest {
}

/// Request for `MemberRemove`
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub struct MemberRemoveRequest {
/// The inner request
inner: xlineapi::MemberRemoveRequest,
Expand All @@ -106,7 +106,7 @@ impl From<MemberRemoveRequest> for xlineapi::MemberRemoveRequest {
}

/// Request for `MemberUpdate`
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub struct MemberUpdateRequest {
/// The inner request
inner: xlineapi::MemberUpdateRequest,
Expand Down
2 changes: 1 addition & 1 deletion xline-client/src/types/kv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ impl From<TxnRequest> for xlineapi::TxnRequest {
/// For example, here is a revision list: [(A, 1), (A, 2), (A, 3), (A, 4), (A, 5)].
/// We compact at revision 3. After the compaction, the revision list will become [(A, 3), (A, 4), (A, 5)].
/// All revisions less than 3 are deleted. The latest revision, 3, will be kept.
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub struct CompactionRequest {
/// The inner request
inner: xlineapi::CompactionRequest,
Expand Down
2 changes: 2 additions & 0 deletions xlinectl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ async-trait = "0.1.68"
clap = { version = "4.3.2", features = ["derive", "string"] }
itertools = "0.10.5"
regex = "1.8.4"
serde = { version = "1.0.137", features = ["derive"] }
serde_json = "1.0.107"
shlex = "1.1.0"
thiserror = "1.0.37"
tokio = { version = "1", features = ["rt"] }
Expand Down
155 changes: 155 additions & 0 deletions xlinectl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,35 @@ failure request:
put key1 "val1"
put key2 "some-val"
```
### COMPACTION
COMPACTION discards all Xline event history prior to a given revision. Since Xline uses a multiversion concurrency control model, it preserves all key updates as event history. When the event history up to some revision is no longer needed, all superseded keys may be compacted away to reclaim storage space in the Xline backend database.

#### Usage

```bash
compaction [options] <revision>
```

#### Options
- physical -- To wait for compaction to physically remove all old revisions

#### Output

```
Compacted
...
```

#### Examples

```bash
# compact revision less than 123
./xlinectl compaction 123
Compacted
# wait for compaction to physically remove all old revisions
./xlinectl compaction 234 --physical
Compacted
```

### WATCH
Watches events stream on keys or prefixes
Expand Down Expand Up @@ -463,6 +492,132 @@ keep_alive [options] <leaseId>

## Cluster maintenance commands

### MEMBER
MEMBER provides commands for managing Xline cluster membership.

### MEMBER ADD
MEMBER ADD introduces a new member into the Xline cluster as a new peer.

#### Usage

```bash
member add [options] <peer_urls>
```

#### Options
- is_learner -- Add as learner


#### Output

```
<member_id>
```

#### Examples
```bash
# Add a member whose addresses are [127.0.0.1:2379, 127.0.0.1:2380]
./xlinectl member add "10.0.0.1:2379,10.0.0.2:2379"
16151281779493828828
```

### MEMBER UPDATE
MEMBER UPDATE sets the peer URLs for an existing member in the Xline cluster.

#### Usage

```bash
member update <ID> <peer_urls>
```

#### Output

```
Member updated
```

#### Examples
```bash
./xlinectl member add "10.0.0.1:2379,10.0.0.2:2379"
16151281779493828828
# Add a member whose addresses are [127.0.0.1:2379, 127.0.0.1:2380]
./xlinectl member update 16151281779493828828 "10.0.0.3:2379,10.0.0.4:2379"
```

### MEMBER LIST
MEMBER ADD introduces a new member into the Xline cluster as a new peer.

#### Usage

```bash
member list
```

#### Options
- linearizable -- to use linearizable fetch


#### Output

```
<member_id1>
<member_id2>
...
```

#### Examples
```bash
# List all members
./xlinectl member list
16151281779493828828
16375331871075283369
16171997749406652082
```

### MEMBER REMOVE
MEMBER REMOVE removes a member of an Xline cluster from participating in cluster consensus.

#### Usage

```bash
member remove <ID>
```

#### Output

```
Member removed
```

#### Examples
```bash
# Remove a member
./xlinectl member remove 16151281779493828828
Member removed
```

### MEMBER PROMOTE
MEMBER PROMOTE promotes a learner of an Xline cluster to member

#### Usage

```bash
member promote <ID>
```

#### Output

```
Member promoted
```

#### Examples
```bash
# Remove a member
./xlinectl member promote 16151281779493828828
Member promoted
```

### SNAPSHOT
Get snapshots of xline nodes

Expand Down
59 changes: 59 additions & 0 deletions xlinectl/src/command/compaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use anyhow::Result;
use clap::{arg, value_parser, ArgMatches, Command};
use xline_client::{types::kv::CompactionRequest, Client};

use crate::utils::printer::Printer;

/// Definition of `compaction` command
pub(crate) fn command() -> Command {
Command::new("compaction")
.about("Discards all Xline event history prior to a given revision")
.arg(arg!(<revision> "The revision to compact").value_parser(value_parser!(i64)))
.arg(arg!(--physical "To wait for compaction to physically remove all old revisions"))
}

/// Build request from matches
pub(crate) fn build_request(matches: &ArgMatches) -> CompactionRequest {
let revision = matches.get_one::<i64>("revision").expect("required");
let physical = matches.get_flag("physical");

let mut request = CompactionRequest::new(*revision);

if physical {
request = request.with_physical();
}

request
}

/// Execute the command
pub(crate) async fn execute(client: &mut Client, matches: &ArgMatches) -> Result<()> {
let req = build_request(matches);
let resp = client.kv_client().compact(req).await?;
resp.print();

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
use crate::test_case_struct;

test_case_struct!(CompactionRequest);

#[test]
fn command_parse_should_be_valid() {
let test_cases = vec![
TestCase::new(vec!["compaction", "123"], Some(CompactionRequest::new(123))),
TestCase::new(
vec!["compaction", "123", "--physical"],
Some(CompactionRequest::new(123).with_physical()),
),
];

for case in test_cases {
case.run_test();
}
}
}
65 changes: 65 additions & 0 deletions xlinectl/src/command/member/add.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use clap::{arg, ArgMatches, Command};
use xline_client::{error::Result, types::cluster::MemberAddRequest, Client};

use crate::utils::printer::Printer;

use super::parse_peer_urls;

/// Definition of `add` command
pub(super) fn command() -> Command {
Command::new("add")
.about("Adds a member into the cluster")
.arg(
arg!(<peer_urls> "Comma separated peer URLs for the new member.")
.value_parser(parse_peer_urls),
)
.arg(arg!(--is_learner "Add as learner"))
}

/// Build request from matches
pub(super) fn build_request(matches: &ArgMatches) -> MemberAddRequest {
let peer_urls = matches
.get_one::<Vec<String>>("peer_urls")
.expect("required");
let is_learner = matches.get_flag("is_learner");

MemberAddRequest::new(peer_urls.clone(), is_learner)
}

/// Execute the command
pub(super) async fn execute(client: &mut Client, matches: &ArgMatches) -> Result<()> {
let request = build_request(matches);
let resp = client.cluster_client().member_add(request).await?;
resp.print();

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
use crate::test_case_struct;

test_case_struct!(MemberAddRequest);

#[test]
fn command_parse_should_be_valid() {
let test_cases = vec![
TestCase::new(
vec!["add", "127.0.0.1:2379", "--is_learner"],
Some(MemberAddRequest::new(["127.0.0.1:2379".to_owned()], true)),
),
TestCase::new(
vec!["add", "127.0.0.1:2379,127.0.0.1:2380"],
Some(MemberAddRequest::new(
["127.0.0.1:2379".to_owned(), "127.0.0.1:2380".to_owned()],
false,
)),
),
];

for case in test_cases {
case.run_test();
}
}
}
Loading