From a679858478b790dbc1ce7ba9340fcfe647f04a22 Mon Sep 17 00:00:00 2001
From: Lykin <137850705+tiny-craft@users.noreply.github.com>
Date: Tue, 23 Jan 2024 11:26:15 +0800
Subject: [PATCH] perf: add silent deletion option to batch delete dialog
---
backend/services/browser_service.go | 49 ++++---
.../components/dialogs/DeleteKeyDialog.vue | 10 +-
frontend/src/langs/en-us.json | 5 +-
frontend/src/langs/zh-cn.json | 5 +-
frontend/src/stores/browser.js | 128 +++++++++++-------
5 files changed, 118 insertions(+), 79 deletions(-)
diff --git a/backend/services/browser_service.go b/backend/services/browser_service.go
index a73e0b8..dfb4e86 100644
--- a/backend/services/browser_service.go
+++ b/backend/services/browser_service.go
@@ -2073,7 +2073,7 @@ func (b *browserService) DeleteOneKey(server string, db int, k any) (resp types.
}
// DeleteKeys delete keys sync with notification
-func (b *browserService) DeleteKeys(server string, db int, ks []any, serialNo string) (resp types.JSResp) {
+func (b *browserService) DeleteKeys(server string, db int, ks []any, notice bool, serialNo string) (resp types.JSResp) {
// connect a new connection to export keys
conf := Connection().getConnection(server)
if conf == nil {
@@ -2093,7 +2093,7 @@ func (b *browserService) DeleteKeys(server string, db int, ks []any, serialNo st
defer cancelFunc()
cancelEvent := "delete:stop:" + serialNo
- runtime.EventsOnce(ctx, cancelEvent, func(data ...any) {
+ cancelStopEvent := runtime.EventsOnce(ctx, cancelEvent, func(data ...any) {
cancelFunc()
})
processEvent := "deleting:" + serialNo
@@ -2104,29 +2104,41 @@ func (b *browserService) DeleteKeys(server string, db int, ks []any, serialNo st
var mutex sync.Mutex
del := func(ctx context.Context, cli redis.UniversalClient) error {
startTime := time.Now().Add(-10 * time.Second)
+ supportUnlink := true
for i, k := range ks {
// emit progress per second
- if i >= total-1 || time.Now().Sub(startTime).Milliseconds() > 100 {
+ if notice && (i >= total-1 || time.Now().Sub(startTime).Milliseconds() > 100) {
startTime = time.Now()
param := map[string]any{
"total": total,
"progress": i + 1,
"processing": k,
}
- runtime.EventsEmit(b.ctx, processEvent, param)
+ runtime.EventsEmit(ctx, processEvent, param)
// do some sleep to prevent blocking the Redis server
time.Sleep(10 * time.Millisecond)
}
key := strutil.DecodeRedisKey(k)
- delErr := cli.Del(ctx, key).Err()
- if err != nil {
- failed.Add(1)
- } else {
- // save deleted key
- mutex.Lock()
- deletedKeys = append(deletedKeys, k)
- mutex.Unlock()
+ var delErr error
+ if supportUnlink {
+ if delErr = cli.Unlink(ctx, key).Err(); delErr != nil {
+ supportUnlink = false
+ delErr = nil
+ }
+ }
+ if !supportUnlink {
+ delErr = cli.Del(ctx, key).Err()
+ }
+ if notice {
+ if delErr != nil {
+ failed.Add(1)
+ } else {
+ // save deleted key
+ mutex.Lock()
+ deletedKeys = append(deletedKeys, k)
+ mutex.Unlock()
+ }
}
if errors.Is(delErr, context.Canceled) || canceled {
canceled = true
@@ -2145,7 +2157,7 @@ func (b *browserService) DeleteKeys(server string, db int, ks []any, serialNo st
err = del(ctx, client)
}
- runtime.EventsOff(ctx, cancelEvent)
+ cancelStopEvent()
resp.Success = true
resp.Data = struct {
Canceled bool `json:"canceled"`
@@ -2189,8 +2201,7 @@ func (b *browserService) ExportKey(server string, db int, ks []any, path string,
writer := csv.NewWriter(file)
defer writer.Flush()
- cancelEvent := "export:stop:" + path
- runtime.EventsOnce(ctx, cancelEvent, func(data ...any) {
+ cancelStopEvent := runtime.EventsOnce(ctx, "export:stop:"+path, func(data ...any) {
cancelFunc()
})
processEvent := "exporting:" + path
@@ -2206,7 +2217,7 @@ func (b *browserService) ExportKey(server string, db int, ks []any, path string,
"progress": i + 1,
"processing": k,
}
- runtime.EventsEmit(b.ctx, processEvent, param)
+ runtime.EventsEmit(ctx, processEvent, param)
}
key := strutil.DecodeRedisKey(k)
@@ -2230,7 +2241,7 @@ func (b *browserService) ExportKey(server string, db int, ks []any, path string,
}
}
- runtime.EventsOff(ctx, cancelEvent)
+ cancelStopEvent()
resp.Success = true
resp.Data = struct {
Canceled bool `json:"canceled"`
@@ -2274,7 +2285,7 @@ func (b *browserService) ImportCSV(server string, db int, path string, conflict
reader := csv.NewReader(file)
cancelEvent := "import:stop:" + path
- runtime.EventsOnce(ctx, cancelEvent, func(data ...any) {
+ cancelStopEvent := runtime.EventsOnce(ctx, cancelEvent, func(data ...any) {
cancelFunc()
})
processEvent := "importing:" + path
@@ -2349,7 +2360,7 @@ func (b *browserService) ImportCSV(server string, db int, path string, conflict
}
}
- runtime.EventsOff(ctx, cancelEvent)
+ cancelStopEvent()
resp.Success = true
resp.Data = struct {
Canceled bool `json:"canceled"`
diff --git a/frontend/src/components/dialogs/DeleteKeyDialog.vue b/frontend/src/components/dialogs/DeleteKeyDialog.vue
index 40d5755..0bb83ec 100644
--- a/frontend/src/components/dialogs/DeleteKeyDialog.vue
+++ b/frontend/src/components/dialogs/DeleteKeyDialog.vue
@@ -74,7 +74,7 @@ const onConfirmDelete = async () => {
deleting.value = true
const { server, db, key, affectedKeys } = deleteForm
await nextTick()
- browserStore.deleteKeys(server, db, affectedKeys).catch((e) => {})
+ browserStore.deleteKeys(server, db, affectedKeys, !deleteForm.async).catch((e) => {})
} catch (e) {
$message.error(e.message)
return
@@ -115,11 +115,9 @@ const onClose = () => {
required>
-
-
-
-
-
+
+ {{ $t('dialogue.key.silent') }}
+
}
*/
- async deleteKeys(server, db, keys) {
- const msgRef = $message.loading('', { duration: 0, closable: true })
+ async deleteKeys(server, db, keys, notice) {
+ const msgRef = $message.loading(i18nGlobal.t('dialogue.delete.deleting'), { duration: 0, closable: true })
let deleted = []
let failCount = 0
let canceled = false
const serialNo = Date.now().valueOf().toString()
- const eventName = 'deleting:' + serialNo
- const cancelEvent = 'delete:stop:' + serialNo
- try {
- let maxProgress = 0
- EventsOn(eventName, ({ total, progress, processing }) => {
- // update delete progress
- if (progress > maxProgress) {
- maxProgress = progress
- }
- const k = decodeRedisKey(processing)
- msgRef.content = i18nGlobal.t('dialogue.delete.doing', {
- key: k,
- index: maxProgress,
- count: total,
- })
- })
- msgRef.onClose = () => {
- EventsEmit(cancelEvent)
+ let maxProgress = 0
+ const cancelEventFn = EventsOn('deleting:' + serialNo, ({ total, progress, processing }) => {
+ // update delete progress
+ if (progress > maxProgress) {
+ maxProgress = progress
}
- const { data, success, msg } = await DeleteKeys(server, db, keys, serialNo)
+ const k = decodeRedisKey(processing)
+ msgRef.content = i18nGlobal.t('dialogue.delete.doing', {
+ key: k,
+ index: maxProgress,
+ count: total,
+ })
+ })
+ msgRef.onClose = () => {
+ EventsEmit('delete:stop:' + serialNo)
+ }
+ try {
+ const { data, success, msg } = await DeleteKeys(server, db, keys, notice, serialNo)
if (success) {
canceled = get(data, 'canceled', false)
deleted = get(data, 'deleted', [])
@@ -1650,8 +1649,8 @@ const useBrowserStore = defineStore('browser', {
$message.error(msg)
}
} finally {
+ cancelEventFn()
msgRef.destroy()
- EventsOff(eventName)
// clear checked keys
const tab = useTabStore()
tab.setCheckedKeys(server)
@@ -1662,13 +1661,40 @@ const useBrowserStore = defineStore('browser', {
$message.info(i18nGlobal.t('dialogue.handle_cancel'))
} else if (failCount <= 0) {
// no fail
- $message.success(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount }))
+ let msg = i18nGlobal.t('dialogue.delete.completed')
+ if (notice) {
+ msg +=
+ '\n' +
+ i18nGlobal.t('dialogue.delete.completed_status', {
+ success: deletedCount,
+ fail: failCount,
+ })
+ }
+ $message.success(msg)
} else if (failCount >= deletedCount) {
// all fail
- $message.error(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount }))
+ let msg = i18nGlobal.t('dialogue.delete.completed')
+ if (notice) {
+ msg +=
+ '\n' +
+ i18nGlobal.t('dialogue.delete.completed_status', {
+ success: deletedCount,
+ fail: failCount,
+ })
+ }
+ $message.error(msg)
} else {
// some fail
- $message.warn(i18nGlobal.t('dialogue.delete.completed', { success: deletedCount, fail: failCount }))
+ let msg = i18nGlobal.t('dialogue.delete.completed')
+ if (notice) {
+ msg +=
+ '\n' +
+ i18nGlobal.t('dialogue.delete.completed_status', {
+ success: deletedCount,
+ fail: failCount,
+ })
+ }
+ $message.warn(msg)
}
// update ui
timeout(100).then(async () => {
@@ -1703,19 +1729,18 @@ const useBrowserStore = defineStore('browser', {
let exported = 0
let failCount = 0
let canceled = false
- const eventName = 'exporting:' + path
- try {
- EventsOn(eventName, ({ total, progress, processing }) => {
- // update export progress
- msgRef.content = i18nGlobal.t('dialogue.export.exporting', {
- // key: decodeRedisKey(processing),
- index: progress,
- count: total,
- })
+ const cancelEventFn = EventsOn('exporting:' + path, ({ total, progress, processing }) => {
+ // update export progress
+ msgRef.content = i18nGlobal.t('dialogue.export.exporting', {
+ // key: decodeRedisKey(processing),
+ index: progress,
+ count: total,
})
- msgRef.onClose = () => {
- EventsEmit('export:stop:' + path)
- }
+ })
+ msgRef.onClose = () => {
+ EventsEmit('export:stop:' + path)
+ }
+ try {
const { data, success, msg } = await ExportKey(server, db, keys, path, expire)
if (success) {
canceled = get(data, 'canceled', false)
@@ -1726,7 +1751,7 @@ const useBrowserStore = defineStore('browser', {
}
} finally {
msgRef.destroy()
- EventsOff(eventName)
+ cancelEventFn()
}
if (canceled) {
$message.info(i18nGlobal.t('dialogue.handle_cancel'))
@@ -1759,19 +1784,18 @@ const useBrowserStore = defineStore('browser', {
let imported = 0
let ignored = 0
let canceled = false
- const eventName = 'importing:' + path
- try {
- EventsOn(eventName, ({ imported = 0, ignored = 0 }) => {
- // update export progress
- msgRef.content = i18nGlobal.t('dialogue.import.importing', {
- // key: decodeRedisKey(processing),
- imported,
- conflict: ignored,
- })
+ const cancelEventFn = EventsOn('importing:' + path, ({ imported = 0, ignored = 0 }) => {
+ // update export progress
+ msgRef.content = i18nGlobal.t('dialogue.import.importing', {
+ // key: decodeRedisKey(processing),
+ imported,
+ conflict: ignored,
})
- msgRef.onClose = () => {
- EventsEmit('import:stop:' + path)
- }
+ })
+ msgRef.onClose = () => {
+ EventsEmit('import:stop:' + path)
+ }
+ try {
const { data, success, msg } = await ImportCSV(server, db, path, conflict, ttl)
if (success) {
canceled = get(data, 'canceled', false)
@@ -1781,8 +1805,8 @@ const useBrowserStore = defineStore('browser', {
$message.error(msg)
}
} finally {
+ cancelEventFn()
msgRef.destroy()
- EventsOff(eventName)
}
if (canceled) {
$message.info(i18nGlobal.t('dialogue.handle_cancel'))