Skip to content

Commit 8bf6eca

Browse files
committed
add model for is_sharded and only backup one shard if the table is not sharded
1 parent 13e800f commit 8bf6eca

File tree

6 files changed

+93
-26
lines changed

6 files changed

+93
-26
lines changed

frontend/src/pages/Backups/Backups.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useEffect, useState } from 'react'
22
import { usePollingEffect } from '../../utils/usePollingEffect'
33
import { ColumnType } from 'antd/es/table'
4-
import { Table, Button, Form, Input, Modal, Tag, Col, Progress, Row, Tooltip, notification } from 'antd'
4+
import { Table, Button, Form, Input, Checkbox, Modal, Tag, Col, Progress, Row, Tooltip, notification } from 'antd'
55
import useSWR, { mutate } from 'swr'
66

77
interface BackupRow {
@@ -29,6 +29,7 @@ type FieldType = {
2929
table?: string
3030
bucket?: string
3131
path?: string
32+
is_sharded?: boolean
3233
aws_access_key_id?: string
3334
aws_secret_access_key?: string
3435
}
@@ -165,6 +166,16 @@ export default function Backups() {
165166
<Input />
166167
</Form.Item>
167168

169+
<Form.Item<FieldType>
170+
label="Is Sharded"
171+
name="is_sharded"
172+
initialValue="false"
173+
valuePropName="checked"
174+
rules={[{ required: true, message: 'Is this table sharded?' }]}
175+
>
176+
<Checkbox defaultChecked={false}>is sharded</Checkbox>
177+
</Form.Item>
178+
168179
<Form.Item<FieldType>
169180
label="S3 Bucket"
170181
name="bucket"

frontend/src/pages/Backups/ScheduledBackups.tsx

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
import React, { useEffect, useState } from 'react'
22
import { usePollingEffect } from '../../utils/usePollingEffect'
33
import { ColumnType } from 'antd/es/table'
4-
import { Switch, Select, Table, Button, Form, Input, Modal, Tag, Col, Progress, Row, Tooltip, notification } from 'antd'
5-
import DeleteOutlined from '@ant-design/icons'
6-
import EditOutlined from '@ant-design/icons'
4+
import {
5+
Switch,
6+
Select,
7+
Table,
8+
Button,
9+
Form,
10+
Input,
11+
Checkbox,
12+
Modal,
13+
Tag,
14+
Col,
15+
Progress,
16+
Row,
17+
Tooltip,
18+
notification,
19+
} from 'antd'
20+
import { DeleteOutlined, EditOutlined } from '@ant-design/icons'
721
import { Clusters } from '../Clusters/Clusters'
822
import useSWR, { mutate } from 'swr'
923

@@ -32,6 +46,7 @@ type FieldType = {
3246
incremental_schedule?: string
3347
database?: string
3448
table?: string
49+
is_sharded?: boolean
3550
bucket?: string
3651
path?: string
3752
aws_access_key_id?: string
@@ -153,12 +168,12 @@ export default function ScheduledBackups() {
153168
{ title: 'Last Run Time', dataIndex: 'last_run_time' },
154169
{ title: 'Database', dataIndex: 'database' },
155170
{ title: 'Table', dataIndex: 'table' },
171+
{ title: 'Is Sharded', dataIndex: 'is_sharded', render: (_, sched) => (sched.is_sharded ? 'Yes' : 'No') },
156172
{ title: 'S3 Location', dataIndex: 'bucket', render: (_, sched) => 's3://' + sched.bucket + '/' + sched.path },
157-
{ title: 'Created At', dataIndex: 'created_at' },
158173
{
159-
title: '',
174+
title: 'Actions',
160175
dataIndex: 'id',
161-
render: id => {
176+
render: (id: string, rowData: ScheduleRow) => {
162177
const deleteBackup = async () => {
163178
try {
164179
const res = await fetch(`/api/scheduled_backups/${id}`, {
@@ -173,21 +188,10 @@ export default function ScheduledBackups() {
173188
}
174189
}
175190

176-
return (
177-
<a id={id} onClick={deleteBackup}>
178-
<DeleteOutlined />
179-
</a>
180-
)
181-
},
182-
},
183-
{
184-
title: 'Actions',
185-
dataIndex: 'id',
186-
render: (id: string, rowData: ScheduleRow) => {
187191
return (
188192
<>
189193
<EditOutlined onClick={() => handleEdit(rowData)} />
190-
{/* <DeleteOutlined onClick={() => handleDelete(id)} /> */}
194+
<DeleteOutlined onClick={() => deleteBackup()} style={{ marginLeft: '15px' }} />
191195
</>
192196
)
193197
},
@@ -285,6 +289,16 @@ export default function ScheduledBackups() {
285289
<Input />
286290
</Form.Item>
287291

292+
<Form.Item<FieldType>
293+
label="Is Sharded"
294+
name="is_sharded"
295+
initialValue="false"
296+
valuePropName="checked"
297+
rules={[{ required: true, message: 'Is this table sharded?' }]}
298+
>
299+
<Checkbox defaultChecked={false}>is sharded</Checkbox>
300+
</Form.Item>
301+
288302
<Form.Item<FieldType>
289303
label="S3 Bucket"
290304
name="bucket"

housewatch/clickhouse/backups.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from typing import Dict, Optional
55
from uuid import uuid4
66
from housewatch.clickhouse.client import run_query
7-
from housewatch.clickhouse.table import is_replicated_table
87
from housewatch.models.backup import ScheduledBackup, ScheduledBackupRun
8+
from housewatch.clickhouse.table import table_engine_full
99
from housewatch.clickhouse.clusters import get_node_per_shard
1010

1111
from django.conf import settings
@@ -26,7 +26,7 @@ def execute_backup(
2626
aws_key: Optional[str] = None,
2727
aws_secret: Optional[str] = None,
2828
base_backup: Optional[str] = None,
29-
is_replicated: bool = False,
29+
is_sharded: bool = False,
3030
):
3131
"""
3232
This function will execute a backup on each shard in a cluster
@@ -59,8 +59,8 @@ def execute_backup(
5959
item[key[0]] = res[index]
6060
response.append(item)
6161
responses.append((shard, response))
62-
if is_replicated:
63-
break
62+
if not is_sharded:
63+
return response
6464
return response
6565

6666

@@ -82,7 +82,9 @@ def get_backup(backup, cluster=None):
8282
return run_query(QUERY, {"uuid": backup}, use_cache=False)
8383

8484

85-
def create_table_backup(database, table, bucket, path, cluster=None, aws_key=None, aws_secret=None, base_backup=None):
85+
def create_table_backup(
86+
database, table, bucket, path, cluster=None, aws_key=None, aws_secret=None, base_backup=None, is_sharded=False
87+
):
8688
if aws_key is None or aws_secret is None:
8789
aws_key = settings.AWS_ACCESS_KEY_ID
8890
aws_secret = settings.AWS_SECRET_ACCESS_KEY
@@ -106,7 +108,7 @@ def create_table_backup(database, table, bucket, path, cluster=None, aws_key=Non
106108
aws_key=aws_key,
107109
aws_secret=aws_secret,
108110
base_backup=base_backup,
109-
is_replicated=is_replicated_table(database, table),
111+
is_sharded=is_sharded,
110112
)
111113
QUERY = """BACKUP TABLE %(database)s.%(table)s
112114
TO S3('https://%(bucket)s.s3.amazonaws.com/%(path)s', '%(aws_key)s', '%(aws_secret)s')
@@ -204,6 +206,7 @@ def run_backup(backup_id, incremental=False):
204206
backup.aws_access_key_id,
205207
backup.aws_secret_access_key,
206208
base_backup=base_backup,
209+
is_sharded=backup.is_sharded,
207210
)
208211
uuid = str(uuid4())
209212
br = ScheduledBackupRun.objects.create(

housewatch/clickhouse/table.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,20 @@
22

33

44
def is_replicated_table(database, table):
5-
QUERY = """SELECT is_replicated FROM system.tables WHERE database = '%(database)s' AND name = '%(table)s'"""
5+
QUERY = """SELECT engine FROM system.tables WHERE database = '%(database)s' AND name = '%(table)s'"""
66
return "replicated" in run_query(QUERY, {"database": database, "table": table})[0]["engine"].lower()
7+
8+
9+
def table_engine_full(database, table):
10+
QUERY = """SELECT engine_full FROM system.tables WHERE database = '%(database)s' AND name = '%(table)s'"""
11+
return run_query(QUERY, {"database": database, "table": table})[0]["engine_full"]
12+
13+
14+
def parse_engine(engine_full):
15+
engine = engine_full.split("(")[0].strip()
16+
params = engine_full.split("(")[1].split(")")[0].split(",")
17+
return engine, params
18+
19+
20+
def is_sharded_table(database, table):
21+
return "sharded" in table_engine_full(database, table).lower()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 4.1.1 on 2024-01-31 06:36
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('housewatch', '0010_scheduledbackup_incremental_schedule_and_more'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='scheduledbackup',
15+
name='is_sharded',
16+
field=models.BooleanField(default=False),
17+
),
18+
migrations.AlterField(
19+
model_name='scheduledbackup',
20+
name='table',
21+
field=models.CharField(blank=True, max_length=255, null=True),
22+
),
23+
]

housewatch/models/backup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class ScheduledBackup(models.Model):
1919
incremental_schedule: models.CharField = models.CharField(max_length=255, null=True)
2020
table: models.CharField = models.CharField(max_length=255, null=True, blank=True)
2121
database: models.CharField = models.CharField(max_length=255)
22+
is_sharded: models.BooleanField = models.BooleanField(default=False)
2223
cluster: models.CharField = models.CharField(max_length=255, null=True)
2324
bucket: models.CharField = models.CharField(max_length=255)
2425
path: models.CharField = models.CharField(max_length=255)

0 commit comments

Comments
 (0)