starrocks-br 0.2.0__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- starrocks_br/cli.py +257 -193
- starrocks_br/concurrency.py +50 -50
- starrocks_br/config.py +31 -23
- starrocks_br/db.py +37 -37
- starrocks_br/executor.py +100 -71
- starrocks_br/health.py +1 -6
- starrocks_br/history.py +5 -6
- starrocks_br/labels.py +14 -10
- starrocks_br/planner.py +119 -113
- starrocks_br/repository.py +3 -5
- starrocks_br/restore.py +240 -187
- starrocks_br/schema.py +20 -16
- starrocks_br/timezone.py +28 -29
- starrocks_br/utils.py +86 -0
- starrocks_br-0.4.0.dist-info/METADATA +152 -0
- starrocks_br-0.4.0.dist-info/RECORD +21 -0
- starrocks_br-0.2.0.dist-info/METADATA +0 -12
- starrocks_br-0.2.0.dist-info/RECORD +0 -20
- {starrocks_br-0.2.0.dist-info → starrocks_br-0.4.0.dist-info}/WHEEL +0 -0
- {starrocks_br-0.2.0.dist-info → starrocks_br-0.4.0.dist-info}/entry_points.txt +0 -0
- {starrocks_br-0.2.0.dist-info → starrocks_br-0.4.0.dist-info}/top_level.txt +0 -0
starrocks_br/schema.py
CHANGED
|
@@ -1,32 +1,33 @@
|
|
|
1
1
|
from . import logger
|
|
2
2
|
|
|
3
|
+
|
|
3
4
|
def initialize_ops_schema(db) -> None:
|
|
4
5
|
"""Initialize the ops database and all required control tables.
|
|
5
|
-
|
|
6
|
+
|
|
6
7
|
Creates empty ops tables. Does NOT populate with sample data.
|
|
7
8
|
Users must manually insert their table inventory records.
|
|
8
9
|
"""
|
|
9
|
-
|
|
10
|
+
|
|
10
11
|
logger.info("Creating ops database...")
|
|
11
12
|
db.execute("CREATE DATABASE IF NOT EXISTS ops")
|
|
12
13
|
logger.success("ops database created")
|
|
13
|
-
|
|
14
|
+
|
|
14
15
|
logger.info("Creating ops.table_inventory...")
|
|
15
16
|
db.execute(get_table_inventory_schema())
|
|
16
17
|
logger.success("ops.table_inventory created")
|
|
17
|
-
|
|
18
|
+
|
|
18
19
|
logger.info("Creating ops.backup_history...")
|
|
19
20
|
db.execute(get_backup_history_schema())
|
|
20
21
|
logger.success("ops.backup_history created")
|
|
21
|
-
|
|
22
|
+
|
|
22
23
|
logger.info("Creating ops.restore_history...")
|
|
23
24
|
db.execute(get_restore_history_schema())
|
|
24
25
|
logger.success("ops.restore_history created")
|
|
25
|
-
|
|
26
|
+
|
|
26
27
|
logger.info("Creating ops.run_status...")
|
|
27
28
|
db.execute(get_run_status_schema())
|
|
28
29
|
logger.success("ops.run_status created")
|
|
29
|
-
|
|
30
|
+
|
|
30
31
|
logger.info("Creating ops.backup_partitions...")
|
|
31
32
|
db.execute(get_backup_partitions_schema())
|
|
32
33
|
logger.success("ops.backup_partitions created")
|
|
@@ -36,26 +37,26 @@ def initialize_ops_schema(db) -> None:
|
|
|
36
37
|
|
|
37
38
|
def ensure_ops_schema(db) -> bool:
|
|
38
39
|
"""Ensure ops schema exists, creating it if necessary.
|
|
39
|
-
|
|
40
|
+
|
|
40
41
|
Returns True if schema was created, False if it already existed.
|
|
41
42
|
This is called automatically before backup/restore operations.
|
|
42
43
|
"""
|
|
43
44
|
try:
|
|
44
45
|
result = db.query("SHOW DATABASES LIKE 'ops'")
|
|
45
|
-
|
|
46
|
+
|
|
46
47
|
if not result:
|
|
47
48
|
initialize_ops_schema(db)
|
|
48
49
|
return True
|
|
49
|
-
|
|
50
|
+
|
|
50
51
|
db.execute("USE ops")
|
|
51
52
|
tables_result = db.query("SHOW TABLES")
|
|
52
|
-
|
|
53
|
+
|
|
53
54
|
if not tables_result or len(tables_result) < 5:
|
|
54
55
|
initialize_ops_schema(db)
|
|
55
56
|
return True
|
|
56
|
-
|
|
57
|
+
|
|
57
58
|
return False
|
|
58
|
-
|
|
59
|
+
|
|
59
60
|
except Exception:
|
|
60
61
|
initialize_ops_schema(db)
|
|
61
62
|
return True
|
|
@@ -71,8 +72,9 @@ def get_table_inventory_schema() -> str:
|
|
|
71
72
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
72
73
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
73
74
|
)
|
|
74
|
-
|
|
75
|
+
UNIQUE KEY (inventory_group, database_name, table_name)
|
|
75
76
|
COMMENT "Inventory groups mapping to databases/tables (supports '*' wildcard)"
|
|
77
|
+
DISTRIBUTED BY HASH(inventory_group)
|
|
76
78
|
"""
|
|
77
79
|
|
|
78
80
|
|
|
@@ -133,12 +135,14 @@ def get_backup_partitions_schema() -> str:
|
|
|
133
135
|
"""Get CREATE TABLE statement for backup_partitions."""
|
|
134
136
|
return """
|
|
135
137
|
CREATE TABLE IF NOT EXISTS ops.backup_partitions (
|
|
138
|
+
key_hash STRING NOT NULL COMMENT "MD5 hash of composite key (label, database_name, table_name, partition_name)",
|
|
136
139
|
label STRING NOT NULL COMMENT "The backup label this partition belongs to. FK to ops.backup_history.label.",
|
|
137
140
|
database_name STRING NOT NULL COMMENT "The name of the database the partition belongs to.",
|
|
138
141
|
table_name STRING NOT NULL COMMENT "The name of the table the partition belongs to.",
|
|
139
142
|
partition_name STRING NOT NULL COMMENT "The name of the specific partition.",
|
|
140
143
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT "Timestamp when this record was created."
|
|
141
144
|
)
|
|
142
|
-
PRIMARY KEY (
|
|
145
|
+
PRIMARY KEY (key_hash)
|
|
143
146
|
COMMENT "Tracks every partition included in a backup snapshot."
|
|
144
|
-
|
|
147
|
+
DISTRIBUTED BY HASH(key_hash)
|
|
148
|
+
"""
|
starrocks_br/timezone.py
CHANGED
|
@@ -5,10 +5,10 @@ from zoneinfo import ZoneInfo
|
|
|
5
5
|
|
|
6
6
|
def get_current_time_in_cluster_tz(cluster_tz: str) -> str:
|
|
7
7
|
"""Get current time formatted in cluster timezone.
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
Args:
|
|
10
10
|
cluster_tz: Timezone string (e.g., 'Asia/Shanghai', 'UTC', '+08:00')
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
Returns:
|
|
13
13
|
Formatted datetime string in 'YYYY-MM-DD HH:MM:SS' format in the cluster timezone
|
|
14
14
|
"""
|
|
@@ -19,58 +19,58 @@ def get_current_time_in_cluster_tz(cluster_tz: str) -> str:
|
|
|
19
19
|
|
|
20
20
|
def parse_datetime_with_tz(dt_str: str, tz: str) -> datetime.datetime:
|
|
21
21
|
"""Parse datetime string assuming the given timezone.
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
Args:
|
|
24
24
|
dt_str: Datetime string in 'YYYY-MM-DD HH:MM:SS' format
|
|
25
25
|
tz: Timezone string (e.g., 'Asia/Shanghai', 'UTC', '+08:00')
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
Returns:
|
|
28
28
|
Timezone-aware datetime object
|
|
29
29
|
"""
|
|
30
30
|
timezone = _get_timezone(tz)
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
dt = datetime.datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S")
|
|
33
33
|
dt = dt.replace(tzinfo=timezone)
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
return dt
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def normalize_datetime_to_tz(dt: datetime.datetime, target_tz: str) -> datetime.datetime:
|
|
39
39
|
"""Convert datetime to target timezone.
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
Args:
|
|
42
42
|
dt: Datetime object (timezone-aware or naive)
|
|
43
43
|
target_tz: Target timezone string (e.g., 'Asia/Shanghai', 'UTC', '+08:00')
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
Returns:
|
|
46
46
|
Timezone-aware datetime object in the target timezone
|
|
47
47
|
"""
|
|
48
48
|
timezone = _get_timezone(target_tz)
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
if dt.tzinfo is None:
|
|
51
51
|
dt = dt.replace(tzinfo=datetime.timezone.utc)
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
dt = dt.astimezone(timezone)
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
return dt
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
def _get_timezone(tz_str: str) -> Union[ZoneInfo, datetime.timezone]:
|
|
59
59
|
"""Get timezone object from timezone string.
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
Handles both named timezones (e.g., 'Asia/Shanghai') and offset strings (e.g., '+08:00', '-05:00').
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
Args:
|
|
64
64
|
tz_str: Timezone string
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
Returns:
|
|
67
67
|
ZoneInfo or timezone object
|
|
68
68
|
"""
|
|
69
69
|
tz_str = tz_str.strip()
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
if tz_str.upper() == "UTC" or tz_str == "+00:00" or tz_str == "-00:00":
|
|
72
72
|
return ZoneInfo("UTC")
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
if tz_str.startswith(("+", "-")):
|
|
75
75
|
try:
|
|
76
76
|
hours, minutes = _parse_offset(tz_str)
|
|
@@ -78,7 +78,7 @@ def _get_timezone(tz_str: str) -> Union[ZoneInfo, datetime.timezone]:
|
|
|
78
78
|
return datetime.timezone(offset)
|
|
79
79
|
except ValueError:
|
|
80
80
|
return ZoneInfo("UTC")
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
try:
|
|
83
83
|
return ZoneInfo(tz_str)
|
|
84
84
|
except Exception:
|
|
@@ -87,13 +87,13 @@ def _get_timezone(tz_str: str) -> Union[ZoneInfo, datetime.timezone]:
|
|
|
87
87
|
|
|
88
88
|
def _parse_offset(offset_str: str) -> tuple[int, int]:
|
|
89
89
|
"""Parse timezone offset string to hours and minutes.
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
Args:
|
|
92
92
|
offset_str: Offset string in format '+HH:MM' or '-HH:MM'
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
Returns:
|
|
95
95
|
Tuple of (hours, minutes)
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
Raises:
|
|
98
98
|
ValueError: If offset string is invalid, including:
|
|
99
99
|
- String length < 6 characters
|
|
@@ -103,23 +103,22 @@ def _parse_offset(offset_str: str) -> tuple[int, int]:
|
|
|
103
103
|
"""
|
|
104
104
|
if len(offset_str) < 6:
|
|
105
105
|
raise ValueError(f"Invalid offset format: {offset_str}")
|
|
106
|
-
|
|
107
|
-
if offset_str[3] !=
|
|
106
|
+
|
|
107
|
+
if offset_str[3] != ":":
|
|
108
108
|
raise ValueError(f"Invalid offset format: {offset_str} (missing colon)")
|
|
109
|
-
|
|
110
|
-
sign = 1 if offset_str[0] ==
|
|
111
|
-
|
|
109
|
+
|
|
110
|
+
sign = 1 if offset_str[0] == "+" else -1
|
|
111
|
+
|
|
112
112
|
try:
|
|
113
113
|
hours = int(offset_str[1:3])
|
|
114
114
|
minutes = int(offset_str[4:6])
|
|
115
115
|
except ValueError as e:
|
|
116
116
|
raise ValueError(f"Invalid offset format: {offset_str} (non-numeric values)") from e
|
|
117
|
-
|
|
117
|
+
|
|
118
118
|
if hours < 0 or hours >= 24:
|
|
119
119
|
raise ValueError(f"Invalid offset format: {offset_str} (hours must be 00-23)")
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
if minutes < 0 or minutes >= 60:
|
|
122
122
|
raise ValueError(f"Invalid offset format: {offset_str} (minutes must be 00-59)")
|
|
123
|
-
|
|
124
|
-
return sign * hours, sign * minutes
|
|
125
123
|
|
|
124
|
+
return sign * hours, sign * minutes
|
starrocks_br/utils.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
def quote_identifier(identifier):
|
|
2
|
+
"""
|
|
3
|
+
Quote a SQL identifier (database, table, or column name) with backticks.
|
|
4
|
+
|
|
5
|
+
Args:
|
|
6
|
+
identifier: The database, table, or column name to quote
|
|
7
|
+
|
|
8
|
+
Returns:
|
|
9
|
+
The identifier wrapped in backticks with internal backticks escaped
|
|
10
|
+
|
|
11
|
+
Raises:
|
|
12
|
+
ValueError: If identifier is None or empty string
|
|
13
|
+
|
|
14
|
+
Examples:
|
|
15
|
+
>>> quote_identifier("my_table")
|
|
16
|
+
'`my_table`'
|
|
17
|
+
>>> quote_identifier("select")
|
|
18
|
+
'`select`'
|
|
19
|
+
>>> quote_identifier("table`with`ticks")
|
|
20
|
+
'`table``with``ticks`'
|
|
21
|
+
"""
|
|
22
|
+
if identifier is None:
|
|
23
|
+
raise ValueError("Identifier cannot be None")
|
|
24
|
+
|
|
25
|
+
if identifier == "":
|
|
26
|
+
raise ValueError("Identifier cannot be empty")
|
|
27
|
+
|
|
28
|
+
escaped = identifier.replace("`", "``")
|
|
29
|
+
return f"`{escaped}`"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def quote_value(value):
|
|
33
|
+
"""
|
|
34
|
+
Quote and escape a SQL string value for safe query interpolation.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
value: The string value to quote and escape
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
The properly quoted and escaped SQL value
|
|
41
|
+
|
|
42
|
+
Examples:
|
|
43
|
+
>>> quote_value("test")
|
|
44
|
+
"'test'"
|
|
45
|
+
>>> quote_value("O'Brien")
|
|
46
|
+
"'O''Brien'"
|
|
47
|
+
>>> quote_value(None)
|
|
48
|
+
'NULL'
|
|
49
|
+
"""
|
|
50
|
+
if value is None:
|
|
51
|
+
return "NULL"
|
|
52
|
+
|
|
53
|
+
value = str(value)
|
|
54
|
+
escaped = value.replace("\\", "\\\\")
|
|
55
|
+
escaped = escaped.replace("'", "''")
|
|
56
|
+
escaped = escaped.replace("\n", "\\n")
|
|
57
|
+
escaped = escaped.replace("\t", "\\t")
|
|
58
|
+
|
|
59
|
+
return f"'{escaped}'"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def build_qualified_table_name(database, table):
|
|
63
|
+
"""
|
|
64
|
+
Build a fully qualified table name with proper quoting.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
database: The database name
|
|
68
|
+
table: The table name
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Fully qualified table name in format `database`.`table`
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
ValueError: If database or table is None or empty
|
|
75
|
+
|
|
76
|
+
Examples:
|
|
77
|
+
>>> build_qualified_table_name("my_db", "my_table")
|
|
78
|
+
'`my_db`.`my_table`'
|
|
79
|
+
"""
|
|
80
|
+
if not database:
|
|
81
|
+
raise ValueError("Database name cannot be empty or None")
|
|
82
|
+
|
|
83
|
+
if not table:
|
|
84
|
+
raise ValueError("Table name cannot be empty or None")
|
|
85
|
+
|
|
86
|
+
return f"{quote_identifier(database)}.{quote_identifier(table)}"
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: starrocks-br
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: StarRocks Backup and Restore automation tool
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: click<9,>=8.1.7
|
|
8
|
+
Requires-Dist: PyYAML<7,>=6.0.1
|
|
9
|
+
Requires-Dist: mysql-connector-python<10,>=9.0.0
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest<9,>=8.3.2; extra == "dev"
|
|
12
|
+
Requires-Dist: pytest-mock<4,>=3.14.0; extra == "dev"
|
|
13
|
+
Requires-Dist: pytest-cov<6,>=5.0.0; extra == "dev"
|
|
14
|
+
Requires-Dist: ruff<1,>=0.8.0; extra == "dev"
|
|
15
|
+
|
|
16
|
+
# StarRocks Backup & Restore
|
|
17
|
+
|
|
18
|
+
Full and incremental backup automation for StarRocks shared-nothing clusters.
|
|
19
|
+
|
|
20
|
+
**Requirements:** StarRocks 3.5+ (shared-nothing mode)
|
|
21
|
+
|
|
22
|
+
📋 **[Release Notes & Changelog](CHANGELOG.md)**
|
|
23
|
+
|
|
24
|
+
## Table of Contents
|
|
25
|
+
|
|
26
|
+
- [Why This Tool?](#why-this-tool)
|
|
27
|
+
- [Documentation](#documentation)
|
|
28
|
+
- [Installation](#installation)
|
|
29
|
+
- [Configuration](#configuration)
|
|
30
|
+
- [Basic Usage](#basic-usage)
|
|
31
|
+
- [How It Works](#how-it-works)
|
|
32
|
+
|
|
33
|
+
## Why This Tool?
|
|
34
|
+
|
|
35
|
+
StarRocks provides native `BACKUP` and `RESTORE` commands, but they only support full backups. For large-scale deployments hosting data at petabyte scale, full backups are not feasible due to time, storage, and network constraints.
|
|
36
|
+
|
|
37
|
+
This tool adds **incremental backup capabilities** to StarRocks by leveraging native partition-based backup features.
|
|
38
|
+
|
|
39
|
+
**What StarRocks doesn't provide:**
|
|
40
|
+
- ❌ **No incremental backups** - You must manually identify changed partitions and build complex backup commands
|
|
41
|
+
- ❌ **No backup history** - No built-in way to track what was backed up, when, or which backups succeeded/failed
|
|
42
|
+
- ❌ **No restore intelligence** - You manually determine which backups are needed for point-in-time recovery
|
|
43
|
+
- ❌ **No organization** - No way to group tables or manage different backup strategies
|
|
44
|
+
- ❌ **No concurrency control** - Multiple backup operations can conflict
|
|
45
|
+
|
|
46
|
+
**What this tool provides:**
|
|
47
|
+
- ✅ **Automatic incremental backups** - Tool detects changed partitions since the last full backup automatically
|
|
48
|
+
- ✅ **Complete operation tracking** - Every backup and restore is logged with status, timestamps, and error details
|
|
49
|
+
- ✅ **Intelligent restore** - Automatically resolves backup chains (full + incremental) for you
|
|
50
|
+
- ✅ **Inventory groups** - Organize tables into groups with different backup strategies
|
|
51
|
+
- ✅ **Job concurrency control** - Prevents conflicting operations
|
|
52
|
+
- ✅ **Safe restores** - Atomic rename mechanism prevents data loss during restore
|
|
53
|
+
- ✅ **Metadata management** - Dedicated `ops` database tracks all backup metadata and partition manifests
|
|
54
|
+
|
|
55
|
+
In short: this tool transforms StarRocks's basic backup/restore commands into a **production-ready incremental backup solution**.
|
|
56
|
+
|
|
57
|
+
## Documentation
|
|
58
|
+
|
|
59
|
+
- **[Getting Started](docs/getting-started.md)** - Step-by-step tutorial
|
|
60
|
+
- **[Core Concepts](docs/core-concepts.md)** - Understand inventory groups, backup types, and restore chains
|
|
61
|
+
- **[Installation](docs/installation.md)** - All installation methods
|
|
62
|
+
- **[Configuration](docs/configuration.md)** - Config file reference and TLS setup
|
|
63
|
+
- **[Commands](docs/commands.md)** - Detailed command reference
|
|
64
|
+
- **[Scheduling & Monitoring](docs/scheduling.md)** - Automate backups and monitor status
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
### Option 1: PyPI
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
python3 -m venv .venv
|
|
72
|
+
source .venv/bin/activate
|
|
73
|
+
pip install starrocks-br
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Option 2: Standalone Executable
|
|
77
|
+
|
|
78
|
+
Download from [releases](https://github.com/deep-bi/starrocks-br/releases/latest):
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Linux
|
|
82
|
+
chmod +x starrocks-br-linux-x86_64
|
|
83
|
+
mv starrocks-br-linux-x86_64 starrocks-br
|
|
84
|
+
./starrocks-br --help
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
See [Installation Guide](docs/installation.md) for all options.
|
|
88
|
+
|
|
89
|
+
## Configuration
|
|
90
|
+
|
|
91
|
+
Create a `config.yaml` file pointing to your StarRocks cluster:
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
host: "127.0.0.1" # StarRocks FE node address
|
|
95
|
+
port: 9030 # MySQL protocol port
|
|
96
|
+
user: "root" # Database user with backup/restore privileges
|
|
97
|
+
database: "your_database" # Database containing tables to backup
|
|
98
|
+
repository: "your_repo_name" # Repository created via CREATE REPOSITORY in StarRocks
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Set password:
|
|
102
|
+
```bash
|
|
103
|
+
export STARROCKS_PASSWORD="your_password"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
See [Configuration Reference](docs/configuration.md) for TLS and advanced options.
|
|
107
|
+
|
|
108
|
+
## Basic Usage
|
|
109
|
+
|
|
110
|
+
**Initialize:**
|
|
111
|
+
```bash
|
|
112
|
+
starrocks-br init --config config.yaml
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Define inventory groups** (in StarRocks):
|
|
116
|
+
```sql
|
|
117
|
+
INSERT INTO ops.table_inventory (inventory_group, database_name, table_name)
|
|
118
|
+
VALUES
|
|
119
|
+
('production', 'mydb', 'users'),
|
|
120
|
+
('production', 'mydb', 'orders');
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Backup:**
|
|
124
|
+
```bash
|
|
125
|
+
# Full backup
|
|
126
|
+
starrocks-br backup full --config config.yaml --group production
|
|
127
|
+
|
|
128
|
+
# Incremental backup (tool detects changed partitions automatically)
|
|
129
|
+
starrocks-br backup incremental --config config.yaml --group production
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Restore:**
|
|
133
|
+
```bash
|
|
134
|
+
# Tool automatically resolves backup chains
|
|
135
|
+
starrocks-br restore --config config.yaml --target-label mydb_20251118_full
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
See [Commands Reference](docs/commands.md) for all options.
|
|
139
|
+
|
|
140
|
+
## How It Works
|
|
141
|
+
|
|
142
|
+
1. **Inventory Groups**: Define collections of tables that share the same backup strategy
|
|
143
|
+
2. **ops Database**: Tool creates an `ops` database to track all operations and metadata
|
|
144
|
+
3. **Automatic Incrementals**: Tool queries partition metadata and compares with the baseline to detect changes
|
|
145
|
+
4. **Intelligent Restore**: Automatically resolves backup chains (full + incremental) for point-in-time recovery
|
|
146
|
+
5. **Safe Operations**: All restores use temporary tables with atomic rename for safety
|
|
147
|
+
|
|
148
|
+
Read [Core Concepts](docs/core-concepts.md) for detailed explanations.
|
|
149
|
+
|
|
150
|
+
## Contributing
|
|
151
|
+
|
|
152
|
+
We welcome contributions! See issues for areas that need help or create a new issue to report a bug or request a feature.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
starrocks_br/__init__.py,sha256=i1m0FIl2IAXaVyNoya0ZNAx3WfhIp9I6VLhTz06qNFY,28
|
|
2
|
+
starrocks_br/cli.py,sha256=yXrmfLL-doIZ9ahFKfXqGR09NB08jqz4BUhkkSCpEJc,20483
|
|
3
|
+
starrocks_br/concurrency.py,sha256=N0LD4VHTAFNhD4YslrkOCDSx5cnR5rCEkNH9MkODxv8,5903
|
|
4
|
+
starrocks_br/config.py,sha256=APqOZcJuUzYmGNHoJRlsu4l3sWl_4SS1kRLKjKm2Oag,2059
|
|
5
|
+
starrocks_br/db.py,sha256=gPFP_VcGmAM5uMADSFR9pNEbYN9IXDnN0O2YFMptkmM,4962
|
|
6
|
+
starrocks_br/executor.py,sha256=Z8k5oW-TRE0zg8on9_YtKqhT82Nq_ZmuGxC09Wx7M60,11367
|
|
7
|
+
starrocks_br/health.py,sha256=rmkgNYf6kk3VDZx-PmnAG3lzmtvnJcUPG7Ppb6BA7IU,1021
|
|
8
|
+
starrocks_br/history.py,sha256=kx2H0wGVYCoIvC1_zN4BMGv9d2YebR5oX3NlUd9yeWg,3005
|
|
9
|
+
starrocks_br/labels.py,sha256=-JfO34gNjYEGjSRW5haRRYYWvMrXRpNh9mTKXthAxzI,1675
|
|
10
|
+
starrocks_br/logger.py,sha256=QTfr-nC3TdeU7f1gcRTRDAQSLYpwaevd_iT1B_RbuF8,900
|
|
11
|
+
starrocks_br/planner.py,sha256=QcgBw_psqzWS2FnsmXvYHFgiOMMkfyEvMJryDUFhkpk,10926
|
|
12
|
+
starrocks_br/repository.py,sha256=gZgT0mAjs-AAdESXPF8Syv0bE8m5njya5leTageElQ8,1251
|
|
13
|
+
starrocks_br/restore.py,sha256=4XBcPMeplrqKyaU_yU5GhunAtPWtmvErOi2xOQPWQKA,20306
|
|
14
|
+
starrocks_br/schema.py,sha256=FSJjcz4q3SU_rHLptsSzrlm-o0dcvIu6LbpT-Z5GyZA,6199
|
|
15
|
+
starrocks_br/timezone.py,sha256=RYmM9RIEy1hphVGhwv7A8j1DyV9Djf-Y5HE8NxZMDtg,3609
|
|
16
|
+
starrocks_br/utils.py,sha256=LF3uBdaNMeslE4UHl_wwv4QErCS48ISxpPqYX8dbrc8,2176
|
|
17
|
+
starrocks_br-0.4.0.dist-info/METADATA,sha256=wYmVX_4K_i5K42fxA075GEUqCBSAbhfbAJ-qnvPx8I8,5675
|
|
18
|
+
starrocks_br-0.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
+
starrocks_br-0.4.0.dist-info/entry_points.txt,sha256=AKUt01G2MAlh85s1Q9kNQDOUio14kaTnT3dmg9gjdNg,54
|
|
20
|
+
starrocks_br-0.4.0.dist-info/top_level.txt,sha256=CU1tGVo0kjulhDr761Sndg-oTeRKsisDnWm8UG95aBE,13
|
|
21
|
+
starrocks_br-0.4.0.dist-info/RECORD,,
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: starrocks-br
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: StarRocks Backup and Restore automation tool
|
|
5
|
-
Requires-Python: >=3.9
|
|
6
|
-
Requires-Dist: click<9,>=8.1.7
|
|
7
|
-
Requires-Dist: PyYAML<7,>=6.0.1
|
|
8
|
-
Requires-Dist: mysql-connector-python<10,>=9.0.0
|
|
9
|
-
Provides-Extra: dev
|
|
10
|
-
Requires-Dist: pytest<9,>=8.3.2; extra == "dev"
|
|
11
|
-
Requires-Dist: pytest-mock<4,>=3.14.0; extra == "dev"
|
|
12
|
-
Requires-Dist: pytest-cov<6,>=5.0.0; extra == "dev"
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
starrocks_br/__init__.py,sha256=i1m0FIl2IAXaVyNoya0ZNAx3WfhIp9I6VLhTz06qNFY,28
|
|
2
|
-
starrocks_br/cli.py,sha256=3FvtMDYra6_9CwxSWj3i4VcIjHlSDO9LbBwC0-Bh8bc,20550
|
|
3
|
-
starrocks_br/concurrency.py,sha256=wx69u-RW1OnukKn6CQ9EJS4L42N9Gzw7Xz7rgETzmy4,5934
|
|
4
|
-
starrocks_br/config.py,sha256=nL57JR4O1WBG6iOscuCX5aui7lOs6PIiS97MIgVDvOM,2036
|
|
5
|
-
starrocks_br/db.py,sha256=UDmN8uIYTMzxx7_D0XxmYSpH3VTwBKEeBNQ-4hI4SnA,5098
|
|
6
|
-
starrocks_br/executor.py,sha256=I9cefPbHaSiajPvoYBVaRXM06UfP6jzVRdS8q_t5zeE,10681
|
|
7
|
-
starrocks_br/health.py,sha256=DpTy4uqk1UrbV0d9Wtk9Ke9K0iT4ndL-01gqqSywR_c,1050
|
|
8
|
-
starrocks_br/history.py,sha256=j6eqkD1MyTvgoztffnLnr6-6VXd0gdvLxLLKxbC1AG0,3016
|
|
9
|
-
starrocks_br/labels.py,sha256=D67JqIUWtFAnuj9thnC4Y7A0tzLk6d4YpBtDGhen1yc,1689
|
|
10
|
-
starrocks_br/logger.py,sha256=QTfr-nC3TdeU7f1gcRTRDAQSLYpwaevd_iT1B_RbuF8,900
|
|
11
|
-
starrocks_br/planner.py,sha256=_e65v5XRoXKJcLPX73UXKtA721TWjw2Txfet-TItXu8,10326
|
|
12
|
-
starrocks_br/repository.py,sha256=6uTJBYgQFEjJBlfhirriTkad80teTPcBSl_tphUExz4,1269
|
|
13
|
-
starrocks_br/restore.py,sha256=_OzlQjPFkH7ySgFSSbpYZHY3A_U1Jz3-UKTwGl2AiQw,18965
|
|
14
|
-
starrocks_br/schema.py,sha256=s_BAUhNgfQscRzhj-OB4xmp19CYZ0ZElyBfdht0_OSE,6114
|
|
15
|
-
starrocks_br/timezone.py,sha256=ONaudOgIfzPdJpZyIUSh0HF5D7oPQAtungNoUqtGM6M,3738
|
|
16
|
-
starrocks_br-0.2.0.dist-info/METADATA,sha256=RnBcIZXdCArx1rXruABPf735XJjb3ZB1jy-egUrKBy0,419
|
|
17
|
-
starrocks_br-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
-
starrocks_br-0.2.0.dist-info/entry_points.txt,sha256=AKUt01G2MAlh85s1Q9kNQDOUio14kaTnT3dmg9gjdNg,54
|
|
19
|
-
starrocks_br-0.2.0.dist-info/top_level.txt,sha256=CU1tGVo0kjulhDr761Sndg-oTeRKsisDnWm8UG95aBE,13
|
|
20
|
-
starrocks_br-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|