perf: optimize the logic for bulk deletion of selected keys
This commit is contained in:
parent
e28bb57a25
commit
21a569d9bb
|
@ -1999,6 +1999,7 @@ func (b *browserService) DeleteKeys(server string, db int, ks []any, serialNo st
|
|||
var deletedKeys = make([]any, 0, total)
|
||||
var mutex sync.Mutex
|
||||
del := func(ctx context.Context, cli redis.UniversalClient) error {
|
||||
startTime := time.Now()
|
||||
for i, k := range ks {
|
||||
// emit progress per second
|
||||
param := map[string]any{
|
||||
|
@ -2006,12 +2007,13 @@ func (b *browserService) DeleteKeys(server string, db int, ks []any, serialNo st
|
|||
"progress": i + 1,
|
||||
"processing": k,
|
||||
}
|
||||
runtime.EventsEmit(b.ctx, processEvent, param)
|
||||
if i >= total-1 || time.Now().Sub(startTime).Milliseconds() > 100 {
|
||||
startTime = time.Now()
|
||||
runtime.EventsEmit(b.ctx, processEvent, param)
|
||||
}
|
||||
|
||||
key := strutil.DecodeRedisKey(k)
|
||||
delErr := cli.Del(ctx, key).Err()
|
||||
// do some sleep to prevent blocking the Redis server
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
if err != nil {
|
||||
failed.Add(1)
|
||||
} else {
|
||||
|
@ -2024,6 +2026,8 @@ func (b *browserService) DeleteKeys(server string, db int, ks []any, serialNo st
|
|||
canceled = true
|
||||
break
|
||||
}
|
||||
// do some sleep to prevent blocking the Redis server
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
"remove_tip": "{type} \"{name}\" will be deleted",
|
||||
"remove_group_tip": "Group \"{name}\" and all connections in it will be deleted",
|
||||
"delete_key_succ": "\"{key}\" has been deleted",
|
||||
"deleting_key": "Deleting key({index}/{count}): {key}",
|
||||
"deleting_key": "Deleting key({index}/{count})",
|
||||
"delete_completed": "Deletion process has been completed, {success} successed, {fail} failed",
|
||||
"rename_binary_key_fail": "Rename binary key name is unsupported",
|
||||
"handle_succ": "Success!",
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
"remove_tip": "{type} \"{name}\" 将会被删除",
|
||||
"remove_group_tip": "分组 \"{name}\"及其所有连接将会被删除",
|
||||
"delete_key_succ": "{key} 已被删除",
|
||||
"deleting_key": "正在删除键({index}/{count}):{key}",
|
||||
"deleting_key": "正在删除键({index}/{count})",
|
||||
"delete_completed": "已完成删除操作,成功{success}个,失败{fail}个",
|
||||
"rename_binary_key_fail": "不支持重命名二进制键名",
|
||||
"handle_succ": "操作成功",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { isEmpty, remove, sortBy, sortedIndexBy, sumBy } from 'lodash'
|
||||
import { isEmpty, remove, size, sortBy, sortedIndexBy, sumBy } from 'lodash'
|
||||
import { ConnectionType } from '@/consts/connection_type.js'
|
||||
|
||||
/**
|
||||
|
@ -95,30 +95,6 @@ export class RedisNodeItem {
|
|||
}
|
||||
}
|
||||
return false
|
||||
|
||||
// let count = 0
|
||||
// if (!isEmpty(this.children)) {
|
||||
// if (skipSort !== true) {
|
||||
// this.sortChildren()
|
||||
// }
|
||||
//
|
||||
// for (const elem of this.children) {
|
||||
// elem.tidy(skipSort)
|
||||
// count += elem.keyCount
|
||||
// }
|
||||
// } else {
|
||||
// if (this.type === ConnectionType.RedisValue) {
|
||||
// count += 1
|
||||
// } else {
|
||||
// // no children in db node or layer node, set count to 0
|
||||
// count = 0
|
||||
// }
|
||||
// }
|
||||
// if (this.keyCount !== count) {
|
||||
// this.keyCount = count
|
||||
// return true
|
||||
// }
|
||||
// return false
|
||||
}
|
||||
|
||||
sortChildren() {
|
||||
|
@ -142,15 +118,14 @@ export class RedisNodeItem {
|
|||
/**
|
||||
*
|
||||
* @param {{}} predicate
|
||||
* @return {number}
|
||||
*/
|
||||
removeChild(predicate) {
|
||||
if (this.type !== ConnectionType.RedisKey) {
|
||||
return
|
||||
if (this.type !== ConnectionType.RedisKey && this.type !== ConnectionType.RedisDB) {
|
||||
return 0
|
||||
}
|
||||
const removed = remove(this.children, predicate)
|
||||
if (this.children.length <= 0) {
|
||||
// TODO: remove from parent if no children
|
||||
}
|
||||
return size(removed)
|
||||
}
|
||||
|
||||
getChildren() {
|
||||
|
|
|
@ -25,7 +25,7 @@ export class RedisServerState {
|
|||
* @param {string|null} typeFilter redis type filter
|
||||
* @param {LoadingState} loadingState all loading state in opened connections map by server and LoadingState
|
||||
* @param {KeyViewType} viewType view type selection for all opened connections group by 'server'
|
||||
* @param {Map<string, RedisNodeItem>} nodeMap map nodes by "key#type"
|
||||
* @param {Map<string, RedisNodeItem>} nodeMap map nodes by "type#key"
|
||||
*/
|
||||
constructor({
|
||||
name,
|
||||
|
@ -341,7 +341,7 @@ export class RedisServerState {
|
|||
* @return
|
||||
*/
|
||||
tidyNode(key, skipResort) {
|
||||
const dbNode = this.getRoot()
|
||||
const rootNode = this.getRoot()
|
||||
const keyParts = split(key, this.separator)
|
||||
const totalParts = size(keyParts)
|
||||
let node
|
||||
|
@ -355,7 +355,7 @@ export class RedisServerState {
|
|||
}
|
||||
}
|
||||
if (node == null) {
|
||||
node = dbNode
|
||||
node = rootNode
|
||||
}
|
||||
const keyCountUpdated = node.tidy(skipResort)
|
||||
if (keyCountUpdated) {
|
||||
|
@ -366,12 +366,23 @@ export class RedisServerState {
|
|||
if (parentNode == null) {
|
||||
break
|
||||
}
|
||||
parentNode.reCalcKeyCount()
|
||||
const count = parentNode.reCalcKeyCount()
|
||||
if (count <= 0) {
|
||||
let anceKeyNode = rootNode
|
||||
// remove from ancestor node
|
||||
if (i > 1) {
|
||||
const anceKey = join(slice(keyParts, 0, i - 1), this.separator)
|
||||
anceKeyNode = this.nodeMap.get(`${ConnectionType.RedisKey}/${anceKey}`)
|
||||
}
|
||||
if (anceKeyNode != null) {
|
||||
anceKeyNode.removeChild({ type: ConnectionType.RedisKey, redisKey: parentKey })
|
||||
}
|
||||
}
|
||||
}
|
||||
// update key count of db
|
||||
const dbInst = this.databases[this.db]
|
||||
if (dbInst != null) {
|
||||
dbInst.keyCount = dbNode.reCalcKeyCount()
|
||||
dbInst.keyCount = rootNode.reCalcKeyCount()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { endsWith, get, isEmpty, map, size } from 'lodash'
|
||||
import { endsWith, get, isEmpty, map, now, size } from 'lodash'
|
||||
import {
|
||||
AddHashField,
|
||||
AddListItem,
|
||||
|
@ -46,6 +46,7 @@ import { EventsEmit, EventsOff, EventsOn } from 'wailsjs/runtime/runtime.js'
|
|||
import { RedisNodeItem } from '@/objects/redisNodeItem.js'
|
||||
import { RedisServerState } from '@/objects/redisServerState.js'
|
||||
import { RedisDatabaseItem } from '@/objects/redisDatabaseItem.js'
|
||||
import { timeout } from '@/utils/promise.js'
|
||||
|
||||
const useBrowserStore = defineStore('browser', {
|
||||
/**
|
||||
|
@ -1537,7 +1538,7 @@ const useBrowserStore = defineStore('browser', {
|
|||
let canceled = false
|
||||
const serialNo = Date.now().valueOf().toString()
|
||||
const eventName = 'deleting:' + serialNo
|
||||
const cancelEvent = 'deleting:' + serialNo
|
||||
const cancelEvent = 'delete:stop:' + serialNo
|
||||
try {
|
||||
let maxProgress = 0
|
||||
EventsOn(eventName, ({ total, progress, processing }) => {
|
||||
|
@ -1551,7 +1552,6 @@ const useBrowserStore = defineStore('browser', {
|
|||
index: maxProgress,
|
||||
count: total,
|
||||
})
|
||||
// this._deleteKeyNode(server, db, k, false)
|
||||
})
|
||||
delMsgRef.onClose = () => {
|
||||
EventsEmit(cancelEvent)
|
||||
|
@ -1573,12 +1573,6 @@ const useBrowserStore = defineStore('browser', {
|
|||
}
|
||||
// refresh model data
|
||||
const deletedCount = size(deleted)
|
||||
/** @type RedisServerState **/
|
||||
const serverInst = this.servers[server]
|
||||
if (serverInst != null) {
|
||||
serverInst.tidyNode('', true)
|
||||
serverInst.updateDBKeyCount(db, -deletedCount)
|
||||
}
|
||||
if (canceled) {
|
||||
$message.info(i18nGlobal.t('dialogue.handle_cancel'))
|
||||
} else if (failCount <= 0) {
|
||||
|
@ -1591,6 +1585,23 @@ const useBrowserStore = defineStore('browser', {
|
|||
// some fail
|
||||
$message.warn(i18nGlobal.t('dialogue.delete_completed', { success: deletedCount, fail: failCount }))
|
||||
}
|
||||
// update ui
|
||||
timeout(100).then(async () => {
|
||||
/** @type RedisServerState **/
|
||||
const serverInst = this.servers[server]
|
||||
if (serverInst != null) {
|
||||
let start = now()
|
||||
for (let i = 0; i < deleted.length; i++) {
|
||||
serverInst.removeKeyNode(deleted[i], false)
|
||||
if (now() - start > 300) {
|
||||
await timeout(100)
|
||||
start = now()
|
||||
}
|
||||
}
|
||||
serverInst.tidyNode('', true)
|
||||
serverInst.updateDBKeyCount(db, -deletedCount)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export const timeout = (ms) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
Loading…
Reference in New Issue