parent
e55bf528be
commit
2160ae37cf
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "sky-migrate"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Sayan Nandan <nandansayan@outlook.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
skytable = { git = "https://github.com/skytable/client-rust.git" }
|
||||||
|
env_logger = "0.9.0"
|
||||||
|
bincode = "1.3.3"
|
||||||
|
log = "0.4.14"
|
||||||
|
clap = { version = "2.33.3", features = ["yaml"] }
|
@ -0,0 +1,30 @@
|
|||||||
|
name: Skytable Migration Tool
|
||||||
|
version: 0.7.0
|
||||||
|
author: Sayan N. <ohsayan@outlook.com>
|
||||||
|
about: |
|
||||||
|
The Skytable migration tool allows users coming from older versions (>=0.6.0)
|
||||||
|
to upgrade their datasets to the latest Skytable version. This tool currently
|
||||||
|
supports versions >= 0.6.0 and upgrading it to 0.7.0. To upgrade, on needs
|
||||||
|
to simply run:
|
||||||
|
sky-migrate --prevdir <lastpath> --new <host>:<port>
|
||||||
|
Where `<lastpath>` is the path to the last installation's data directory and
|
||||||
|
`<host>` and `<port>` is the hostname and port for the new server instance
|
||||||
|
args:
|
||||||
|
- new:
|
||||||
|
long: new
|
||||||
|
takes_value: true
|
||||||
|
required: true
|
||||||
|
help: The <host>:<port> combo for the new instance
|
||||||
|
value_name: new
|
||||||
|
- prevdir:
|
||||||
|
long: prevdir
|
||||||
|
takes_value: true
|
||||||
|
required: true
|
||||||
|
help: Path to the previous installation location
|
||||||
|
value_name: prevdir
|
||||||
|
- serial:
|
||||||
|
long: serial
|
||||||
|
takes_value: false
|
||||||
|
required: false
|
||||||
|
help: |
|
||||||
|
Transfer entries one-by-one instead of all at once to save memory
|
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Created on Tue Aug 17 2021
|
||||||
|
*
|
||||||
|
* This file is a part of Skytable
|
||||||
|
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
|
||||||
|
* NoSQL database written by Sayan Nandan ("the Author") with the
|
||||||
|
* vision to provide flexibility in data modelling without compromising
|
||||||
|
* on performance, queryability or scalability.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021, Sayan Nandan <ohsayan@outlook.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#![allow(clippy::unit_arg)]
|
||||||
|
|
||||||
|
use clap::{load_yaml, App};
|
||||||
|
use core::hint::unreachable_unchecked;
|
||||||
|
use env_logger::Builder;
|
||||||
|
use log::error as err;
|
||||||
|
use log::info;
|
||||||
|
use skytable::query;
|
||||||
|
use skytable::sync::Connection;
|
||||||
|
use skytable::Query;
|
||||||
|
use skytable::RespCode;
|
||||||
|
use skytable::{Element, Response};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process;
|
||||||
|
type Bytes = Vec<u8>;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// first evaluate config
|
||||||
|
let cfg_layout = load_yaml!("cli.yml");
|
||||||
|
let matches = App::from_yaml(cfg_layout).get_matches();
|
||||||
|
Builder::new()
|
||||||
|
.parse_filters(&env::var("SKY_LOG").unwrap_or_else(|_| "info".to_owned()))
|
||||||
|
.init();
|
||||||
|
let new_host = matches
|
||||||
|
.value_of("new")
|
||||||
|
.map(|v| v.to_string())
|
||||||
|
.unwrap_or_else(|| unsafe { unreachable_unchecked() });
|
||||||
|
let serial = matches.is_present("serial");
|
||||||
|
let hostsplit: Vec<&str> = new_host.split(':').collect();
|
||||||
|
if hostsplit.len() != 2 {
|
||||||
|
err(err!("Bad value for --new"));
|
||||||
|
}
|
||||||
|
let (host, port) = unsafe { (hostsplit.get_unchecked(0), hostsplit.get_unchecked(1)) };
|
||||||
|
let port = match port.parse() {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => err(err!("Bad value for port in --new: {}", e)),
|
||||||
|
};
|
||||||
|
let mut old_dir = matches
|
||||||
|
.value_of("prevdir")
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| unsafe { unreachable_unchecked() });
|
||||||
|
old_dir.push("data.bin");
|
||||||
|
// now connect
|
||||||
|
let mut con = match Connection::new(host, port) {
|
||||||
|
Ok(con) => con,
|
||||||
|
Err(e) => err(err!("Failed to connect to new instance with error: {}", e)),
|
||||||
|
};
|
||||||
|
// run sanity test
|
||||||
|
let q = query!("HEYA");
|
||||||
|
match con.run_simple_query(&q) {
|
||||||
|
Ok(Response::Item(Element::Str(s))) if s.eq("HEY!") => {}
|
||||||
|
Ok(_) => err(err!("Unknown response from server")),
|
||||||
|
Err(e) => err(err!(
|
||||||
|
"An I/O error occurred while running sanity test: {}",
|
||||||
|
e
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
info!("Sanity test complete");
|
||||||
|
|
||||||
|
// now de old file
|
||||||
|
let read = match fs::read(old_dir) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => err(err!(
|
||||||
|
"Failed to read data.bin file from old directory: {}",
|
||||||
|
e
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
let de: HashMap<Bytes, Bytes> = match bincode::deserialize(&read) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => err(err!("Failed to unpack old file with: {}", e)),
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
if serial {
|
||||||
|
// transfer serially
|
||||||
|
for (key, value) in de.into_iter() {
|
||||||
|
let q = query!(
|
||||||
|
"USET",
|
||||||
|
String::from_utf8_unchecked(key),
|
||||||
|
String::from_utf8_unchecked(value)
|
||||||
|
);
|
||||||
|
okay(&mut con, q)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// transfer all at once
|
||||||
|
let mut query = Query::from("USET");
|
||||||
|
for (key, value) in de.into_iter() {
|
||||||
|
query.push(String::from_utf8_unchecked(key));
|
||||||
|
query.push(String::from_utf8_unchecked(value));
|
||||||
|
}
|
||||||
|
okay(&mut con, query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!("Finished migration");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn err(_i: ()) -> ! {
|
||||||
|
process::exit(0x01)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn okay(con: &mut Connection, q: Query) {
|
||||||
|
match con.run_simple_query(&q) {
|
||||||
|
Ok(Response::Item(Element::RespCode(RespCode::Okay))) => {}
|
||||||
|
Err(e) => err(err!("An I/O error occurred while running query: {}", e)),
|
||||||
|
Ok(_) => err(err!("Unknown response from server")),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue