Commit 44c0e9e8 by Archer Committed by GitHub

perf: redis del;perf: cron app run (#6113)

* cron app run

* perf: redis del
parent 7f715a87
...@@ -10,10 +10,12 @@ description: 'FastGPT V4.14.5 更新说明' ...@@ -10,10 +10,12 @@ description: 'FastGPT V4.14.5 更新说明'
## ⚙️ 优化 ## ⚙️ 优化
1. 优化获取 redis 所有 key 的逻辑,避免大量获取时导致阻塞。
## 🐛 修复 ## 🐛 修复
1. MCP 工具创建时,使用自定义鉴权头会报错。 1. MCP 工具创建时,使用自定义鉴权头会报错。
2. 获取对话日志列表时,如果用户头像为空,会抛错。
## 插件 ## 插件
...@@ -120,7 +120,7 @@ ...@@ -120,7 +120,7 @@
"document/content/docs/upgrading/4-14/4142.mdx": "2025-11-18T19:27:14+08:00", "document/content/docs/upgrading/4-14/4142.mdx": "2025-11-18T19:27:14+08:00",
"document/content/docs/upgrading/4-14/4143.mdx": "2025-11-26T20:52:05+08:00", "document/content/docs/upgrading/4-14/4143.mdx": "2025-11-26T20:52:05+08:00",
"document/content/docs/upgrading/4-14/4144.mdx": "2025-12-16T14:56:04+08:00", "document/content/docs/upgrading/4-14/4144.mdx": "2025-12-16T14:56:04+08:00",
"document/content/docs/upgrading/4-14/4145.mdx": "2025-12-17T17:44:38+08:00", "document/content/docs/upgrading/4-14/4145.mdx": "2025-12-18T10:17:10+08:00",
"document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00",
......
...@@ -24,9 +24,7 @@ export const refreshVersionKey = async (key: `${SystemCacheKeyEnum}`, id?: strin ...@@ -24,9 +24,7 @@ export const refreshVersionKey = async (key: `${SystemCacheKeyEnum}`, id?: strin
const keys = await getAllKeysByPrefix(pattern); const keys = await getAllKeysByPrefix(pattern);
if (keys.length > 0) { if (keys.length > 0) {
const pipeline = redis.pipeline(); const pipeline = redis.pipeline();
for (const k of keys) { pipeline.del(keys);
pipeline.del(k);
}
await pipeline.exec(); await pipeline.exec();
} }
} else { } else {
......
...@@ -50,6 +50,15 @@ const nextConfig = { ...@@ -50,6 +50,15 @@ const nextConfig = {
}, },
webpack(config, { isServer, nextRuntime }) { webpack(config, { isServer, nextRuntime }) {
// Ignore autoprefixer warnings from third-party libraries
config.ignoreWarnings = [
...(config.ignoreWarnings || []),
{
module: /@scalar\/api-reference-react/,
message: /autoprefixer/,
},
];
Object.assign(config.resolve.alias, { Object.assign(config.resolve.alias, {
'@mongodb-js/zstd': false, '@mongodb-js/zstd': false,
'@aws-sdk/credential-providers': false, '@aws-sdk/credential-providers': false,
......
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time'; import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time';
import { getNanoid } from '@fastgpt/global/common/string/tools'; import { getNanoid } from '@fastgpt/global/common/string/tools';
import { delay, retryFn } from '@fastgpt/global/common/system/utils'; import { batchRun, retryFn } from '@fastgpt/global/common/system/utils';
import { import {
ChatItemValueTypeEnum, ChatItemValueTypeEnum,
ChatRoleEnum, ChatRoleEnum,
ChatSourceEnum ChatSourceEnum
} from '@fastgpt/global/core/chat/constants'; } from '@fastgpt/global/core/chat/constants';
import { type UserChatItemValueItemType } from '@fastgpt/global/core/chat/type'; import type {
AIChatItemValueItemType,
UserChatItemValueItemType,
ChatHistoryItemResType
} from '@fastgpt/global/core/chat/type';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { import {
getWorkflowEntryNodeIds, getWorkflowEntryNodeIds,
...@@ -21,7 +25,6 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/version/controlle ...@@ -21,7 +25,6 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/version/controlle
import { saveChat } from '@fastgpt/service/core/chat/saveChat'; import { saveChat } from '@fastgpt/service/core/chat/saveChat';
import { WORKFLOW_MAX_RUN_TIMES } from '@fastgpt/service/core/workflow/constants'; import { WORKFLOW_MAX_RUN_TIMES } from '@fastgpt/service/core/workflow/constants';
import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch'; import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
import { getUserChatInfo } from '@fastgpt/service/support/user/team/utils';
import { getRunningUserInfoByTmbId } from '@fastgpt/service/support/user/team/utils'; import { getRunningUserInfoByTmbId } from '@fastgpt/service/support/user/team/utils';
import { createChatUsageRecord } from '@fastgpt/service/support/wallet/usage/controller'; import { createChatUsageRecord } from '@fastgpt/service/support/wallet/usage/controller';
...@@ -30,19 +33,28 @@ export const getScheduleTriggerApp = async () => { ...@@ -30,19 +33,28 @@ export const getScheduleTriggerApp = async () => {
// 1. Find all the app // 1. Find all the app
const apps = await retryFn(() => { const apps = await retryFn(() => {
return MongoApp.find({ return MongoApp.find(
scheduledTriggerConfig: { $exists: true }, {
scheduledTriggerNextTime: { $lte: new Date() } scheduledTriggerConfig: { $exists: true },
}); scheduledTriggerNextTime: { $lte: new Date() }
},
{
_id: 1,
scheduledTriggerConfig: 1,
scheduledTriggerNextTime: 1,
name: 1,
teamId: 1,
tmbId: 1
}
).lean();
}); });
// 2. Run apps // 2. Run apps
await Promise.allSettled( await batchRun(
apps.map(async (app) => { apps,
async (app) => {
if (!app.scheduledTriggerConfig) return; if (!app.scheduledTriggerConfig) return;
const chatId = getNanoid(); const chatId = getNanoid();
// random delay 0 ~ 60s
await delay(Math.floor(Math.random() * 60 * 1000));
// Get app latest version // Get app latest version
const { versionId, nodes, edges, chatConfig } = await retryFn(() => const { versionId, nodes, edges, chatConfig } = await retryFn(() =>
...@@ -52,7 +64,7 @@ export const getScheduleTriggerApp = async () => { ...@@ -52,7 +64,7 @@ export const getScheduleTriggerApp = async () => {
{ {
type: ChatItemValueTypeEnum.text, type: ChatItemValueTypeEnum.text,
text: { text: {
content: app.scheduledTriggerConfig?.defaultPrompt content: app.scheduledTriggerConfig.defaultPrompt || ''
} }
} }
]; ];
...@@ -67,8 +79,47 @@ export const getScheduleTriggerApp = async () => { ...@@ -67,8 +79,47 @@ export const getScheduleTriggerApp = async () => {
}) })
); );
const onSave = async ({
error,
durationSeconds = 0,
assistantResponses = [],
flowResponses = [],
system_memories
}: {
error?: any;
durationSeconds?: number;
assistantResponses?: AIChatItemValueItemType[];
flowResponses?: ChatHistoryItemResType[];
system_memories?: Record<string, any>;
}) =>
saveChat({
chatId,
appId: app._id,
versionId,
teamId: String(app.teamId),
tmbId: String(app.tmbId),
nodes,
appChatConfig: chatConfig,
variables: {},
isUpdateUseTime: false, // owner update use time
newTitle: 'Cron Job',
source: ChatSourceEnum.cronJob,
userContent: {
obj: ChatRoleEnum.Human,
value: userQuery
},
aiContent: {
obj: ChatRoleEnum.AI,
value: assistantResponses,
[DispatchNodeResponseKeyEnum.nodeResponse]: flowResponses,
memories: system_memories
},
durationSeconds,
errorMsg: getErrText(error)
});
try { try {
const { flowUsages, assistantResponses, flowResponses, durationSeconds, system_memories } = const { assistantResponses, flowResponses, durationSeconds, system_memories } =
await retryFn(async () => { await retryFn(async () => {
return dispatchWorkFlow({ return dispatchWorkFlow({
chatId, chatId,
...@@ -96,62 +147,29 @@ export const getScheduleTriggerApp = async () => { ...@@ -96,62 +147,29 @@ export const getScheduleTriggerApp = async () => {
const error = flowResponses[flowResponses.length - 1]?.error; const error = flowResponses[flowResponses.length - 1]?.error;
// Save chat // Save chat
await saveChat({ await onSave({
chatId, error,
appId: app._id,
versionId,
teamId: String(app.teamId),
tmbId: String(app.tmbId),
nodes,
appChatConfig: chatConfig,
variables: {},
isUpdateUseTime: false, // owner update use time
newTitle: 'Cron Job',
source: ChatSourceEnum.cronJob,
userContent: {
obj: ChatRoleEnum.Human,
value: userQuery
},
aiContent: {
obj: ChatRoleEnum.AI,
value: assistantResponses,
[DispatchNodeResponseKeyEnum.nodeResponse]: flowResponses,
memories: system_memories
},
durationSeconds, durationSeconds,
errorMsg: getErrText(error) assistantResponses,
flowResponses,
system_memories
}); });
} catch (error) { } catch (error) {
addLog.error('Schedule trigger error', error); addLog.error('[Schedule app] run error', error);
await saveChat({ await onSave({
chatId, error
appId: app._id, }).catch();
teamId: String(app.teamId), } finally {
tmbId: String(app.tmbId), // update next time
nodes, const nextTime = getNextTimeByCronStringAndTimezone(app.scheduledTriggerConfig);
appChatConfig: chatConfig, await retryFn(() =>
variables: {}, MongoApp.updateOne({ _id: app._id }, { $set: { scheduledTriggerNextTime: nextTime } })
isUpdateUseTime: false, // owner update use time ).catch((err) => {
newTitle: 'Cron Job', addLog.error(`[Schedule app] error update next time`, err);
source: ChatSourceEnum.cronJob,
userContent: {
obj: ChatRoleEnum.Human,
value: userQuery
},
aiContent: {
obj: ChatRoleEnum.AI,
value: [],
[DispatchNodeResponseKeyEnum.nodeResponse]: []
},
durationSeconds: 0,
errorMsg: getErrText(error)
}); });
} }
},
// update next time 50
app.scheduledTriggerNextTime = getNextTimeByCronStringAndTimezone(app.scheduledTriggerConfig);
await app.save().catch();
})
); );
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment