mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-06 18:41:42 +08:00
🐛 fix(redis-scan): 修复大数据量下命名空间加载不完整问题
- 前后端 Redis SCAN 游标统一为字符串传递,避免 Number 精度丢失 - RedisScanKeys 增加 string/number 游标兼容解析,异常游标降级并告警 - 新增游标解析单测 - refs #135
This commit is contained in:
@@ -281,6 +281,23 @@ const getRedisScanLoadCount = (pattern: string, append: boolean): number => {
|
||||
return append ? REDIS_KEY_SEARCH_LOAD_MORE_COUNT : REDIS_KEY_SEARCH_INITIAL_LOAD_COUNT;
|
||||
};
|
||||
|
||||
const normalizeRedisCursor = (value: unknown): string => {
|
||||
if (typeof value === 'string') {
|
||||
const trimmed = value.trim();
|
||||
return trimmed === '' ? '0' : trimmed;
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
if (!Number.isFinite(value)) {
|
||||
return '0';
|
||||
}
|
||||
return Math.trunc(value).toString();
|
||||
}
|
||||
if (typeof value === 'bigint') {
|
||||
return value.toString();
|
||||
}
|
||||
return '0';
|
||||
};
|
||||
|
||||
const normalizeKeySegment = (segment: string): string => {
|
||||
return segment === '' ? EMPTY_SEGMENT_LABEL : segment;
|
||||
};
|
||||
@@ -384,7 +401,7 @@ const RedisViewer: React.FC<RedisViewerProps> = ({ connectionId, redisDB }) => {
|
||||
const [keys, setKeys] = useState<RedisKeyInfo[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [searchPattern, setSearchPattern] = useState('*');
|
||||
const [cursor, setCursor] = useState<number>(0);
|
||||
const [cursor, setCursor] = useState<string>('0');
|
||||
const [hasMore, setHasMore] = useState(false);
|
||||
const [selectedKey, setSelectedKey] = useState<string | null>(null);
|
||||
const [keyValue, setKeyValue] = useState<RedisValue | null>(null);
|
||||
@@ -433,7 +450,7 @@ const RedisViewer: React.FC<RedisViewerProps> = ({ connectionId, redisDB }) => {
|
||||
|
||||
const loadKeys = useCallback(async (
|
||||
pattern: string = '*',
|
||||
fromCursor: number = 0,
|
||||
fromCursor: string = '0',
|
||||
append: boolean = false,
|
||||
targetCount?: number
|
||||
) => {
|
||||
@@ -454,7 +471,7 @@ const RedisViewer: React.FC<RedisViewerProps> = ({ connectionId, redisDB }) => {
|
||||
if (res.success) {
|
||||
const result = res.data;
|
||||
const scannedKeys = Array.isArray(result?.keys) ? result.keys : [];
|
||||
const nextCursor = Number(result?.cursor || 0);
|
||||
const nextCursor = normalizeRedisCursor(result?.cursor);
|
||||
if (append) {
|
||||
setKeys(prev => {
|
||||
const keyMap = new Map<string, RedisKeyInfo>();
|
||||
@@ -466,7 +483,7 @@ const RedisViewer: React.FC<RedisViewerProps> = ({ connectionId, redisDB }) => {
|
||||
setKeys(scannedKeys);
|
||||
}
|
||||
setCursor(nextCursor);
|
||||
setHasMore(nextCursor !== 0);
|
||||
setHasMore(nextCursor !== '0');
|
||||
} else {
|
||||
message.error('加载 Key 失败: ' + res.message);
|
||||
}
|
||||
@@ -483,14 +500,14 @@ const RedisViewer: React.FC<RedisViewerProps> = ({ connectionId, redisDB }) => {
|
||||
}, [getConfig]);
|
||||
|
||||
useEffect(() => {
|
||||
loadKeys(searchPattern, 0, false, getRedisScanLoadCount(searchPattern, false));
|
||||
loadKeys(searchPattern, '0', false, getRedisScanLoadCount(searchPattern, false));
|
||||
}, [redisDB]);
|
||||
|
||||
const handleSearch = (value: string) => {
|
||||
const pattern = value.trim() || '*';
|
||||
setSearchPattern(pattern);
|
||||
setCursor(0);
|
||||
loadKeys(pattern, 0, false, getRedisScanLoadCount(pattern, false));
|
||||
setCursor('0');
|
||||
loadKeys(pattern, '0', false, getRedisScanLoadCount(pattern, false));
|
||||
};
|
||||
|
||||
const handleLoadMore = () => {
|
||||
@@ -501,8 +518,8 @@ const RedisViewer: React.FC<RedisViewerProps> = ({ connectionId, redisDB }) => {
|
||||
};
|
||||
|
||||
const handleRefresh = () => {
|
||||
setCursor(0);
|
||||
loadKeys(searchPattern, 0, false, getRedisScanLoadCount(searchPattern, false));
|
||||
setCursor('0');
|
||||
loadKeys(searchPattern, '0', false, getRedisScanLoadCount(searchPattern, false));
|
||||
};
|
||||
|
||||
const loadKeyValue = async (key: string) => {
|
||||
|
||||
@@ -137,7 +137,7 @@ export interface RedisKeyInfo {
|
||||
|
||||
export interface RedisScanResult {
|
||||
keys: RedisKeyInfo[];
|
||||
cursor: number;
|
||||
cursor: string;
|
||||
}
|
||||
|
||||
export interface RedisValue {
|
||||
|
||||
2
frontend/wailsjs/go/app/App.d.ts
vendored
2
frontend/wailsjs/go/app/App.d.ts
vendored
@@ -124,7 +124,7 @@ export function RedisListSet(arg1:connection.ConnectionConfig,arg2:string,arg3:n
|
||||
|
||||
export function RedisRenameKey(arg1:connection.ConnectionConfig,arg2:string,arg3:string):Promise<connection.QueryResult>;
|
||||
|
||||
export function RedisScanKeys(arg1:connection.ConnectionConfig,arg2:string,arg3:number,arg4:number):Promise<connection.QueryResult>;
|
||||
export function RedisScanKeys(arg1:connection.ConnectionConfig,arg2:string,arg3:string|number,arg4:number):Promise<connection.QueryResult>;
|
||||
|
||||
export function RedisSelectDB(arg1:connection.ConnectionConfig,arg2:number):Promise<connection.QueryResult>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user