8. 数据库编辑安全和权限验证机制
数据库编辑功能的安全机制确保用户只能在具有适当权限的情况下修改数据库结构,同时防止恶意操作和数据冲突。系统实现了多层次的安全防护,包括身份认证、权限验证、并发控制和操作审计。
8.1 数据库编辑身份认证中间件
数据库编辑功能的身份认证中间件确保只有经过认证的用户才能访问数据库编辑接口,同时验证用户的会话状态和令牌有效性。
go
// DatabaseEditAuthMiddleware 数据库编辑身份认证中间件
func DatabaseEditAuthMiddleware() app.HandlerFunc {
return func(c *app.RequestContext) {
// 1. 获取用户会话信息
userID := ctxutil.GetUIDFromCtx(c)
if userID == nil || *userID <= 0 {
c.AbortWithStatusJSON(401, common.ErrResponse{
Code: errno.ErrAuthCode,
Message: "未登录或登录已过期,请重新登录",
})
return
}
// 2. 验证会话有效性
sessionValid, err := validateSession(c, *userID)
if err != nil || !sessionValid {
c.AbortWithStatusJSON(401, common.ErrResponse{
Code: errno.ErrAuthSessionExpiredCode,
Message: "会话已过期,请重新登录",
})
return
}
// 3. 记录用户访问日志
logs.CtxInfof(c, "数据库编辑请求身份验证通过,userID=%d", *userID)
c.Next()
}
}
// validateSession 验证会话有效性
func validateSession(c *app.RequestContext, userID int64) (bool, error) {
// 从上下文获取会话信息
sessionID := c.GetString("session_id")
if sessionID == "" {
return false, fmt.Errorf("session_id not found")
}
// 调用会话服务验证会话
return sessionService.ValidateSession(c, sessionID, userID)
}
8.2 数据库编辑权限管理
数据库编辑权限管理系统基于RBAC(基于角色的访问控制)模型实现了细粒度的权限控制,确保用户只能在授权范围内修改数据库结构。
go
// validateDatabaseEditPermission 验证数据库编辑权限
func validateDatabaseEditPermission(ctx context.Context, userID int64, databaseID string, operation string) error {
// 1. 获取用户角色和权限
userRoles, err := permissionService.GetUserRoles(ctx, userID)
if err != nil {
return fmt.Errorf("获取用户角色失败: %w", err)
}
// 2. 获取数据库所属信息
dbInfo, err := databaseService.GetDatabaseInfo(ctx, databaseID)
if err != nil {
return fmt.Errorf("获取数据库信息失败: %w", err)
}
// 3. 检查用户是否为数据库创建者
if dbInfo.CreatorID == userID {
return nil // 创建者拥有所有编辑权限
}
// 4. 检查用户角色权限
for _, role := range userRoles {
hasPermission, err := permissionService.CheckRolePermission(
ctx,
role.ID,
"database",
databaseID,
operation,
)
if err != nil {
return fmt.Errorf("检查权限失败: %w", err)
}
if hasPermission {
return nil
}
}
// 5. 检查团队权限
hasTeamPermission, err := checkTeamPermission(ctx, userID, dbInfo.TeamID, operation)
if err != nil {
return fmt.Errorf("检查团队权限失败: %w", err)
}
if !hasTeamPermission {
return errors.New("用户没有足够权限执行此操作")
}
return nil
}
8.3 数据库编辑API访问控制
数据库编辑API实现了严格的访问控制机制,包括参数验证、操作频率限制和安全检查,确保编辑操作的安全性和稳定性。
go
// validateDatabaseEditRequest 验证数据库编辑请求参数
func validateDatabaseEditRequest(ctx context.Context, req *pb.UpdateDatabaseRequest) error {
// 1. 参数基本验证
if req.DatabaseId == "" {
return errors.New("数据库ID不能为空")
}
if req.Name != nil && *req.Name == "" {
return errors.New("数据库名称不能为空")
}
// 2. 验证操作频率
userID := ctxutil.GetUIDFromCtx(ctx)
if userID != nil {
limitExceeded, err := rateLimiter.CheckOperationLimit(ctx, *userID, "database_edit")
if err != nil {
return fmt.Errorf("检查操作频率失败: %w", err)
}
if limitExceeded {
return errors.New("操作过于频繁,请稍后再试")
}
}
// 3. 验证数据库是否存在
exists, err := databaseService.CheckDatabaseExists(ctx, req.DatabaseId)
if err != nil {
return fmt.Errorf("检查数据库存在性失败: %w", err)
}
if !exists {
return errors.New("数据库不存在")
}
return nil
}
8.4 数据库编辑安全检查
数据库编辑操作执行前会进行全面的安全检查,确保操作不会对系统稳定性和数据完整性造成负面影响。
go
// validateDatabaseEditSafety 验证数据库编辑安全性
func validateDatabaseEditSafety(ctx context.Context, req *pb.UpdateDatabaseRequest) error {
// 1. 检查数据库结构变更风险
if req.StructureChanges != nil && len(req.StructureChanges) > 0 {
riskLevel, err := assessStructureChangeRisk(req.StructureChanges)
if err != nil {
return fmt.Errorf("评估结构变更风险失败: %w", err)
}
// 高风险操作需要额外确认
if riskLevel >= RiskLevelHigh {
confirmed, err := checkEditConfirmation(ctx, req.DatabaseId, riskLevel)
if err != nil {
return fmt.Errorf("检查操作确认状态失败: %w", err)
}
if !confirmed {
return errors.New("高风险操作需要确认")
}
}
}
// 2. 检查并发编辑冲突
isLocked, err := checkDatabaseLock(ctx, req.DatabaseId)
if err != nil {
return fmt.Errorf("检查数据库锁定状态失败: %w", err)
}
if isLocked {
return errors.New("数据库正在被其他用户编辑,请稍后再试")
}
// 3. 检查存储配额
quotaExceeded, err := checkStorageQuota(ctx, req)
if err != nil {
return fmt.Errorf("检查存储配额失败: %w", err)
}
if quotaExceeded {
return errors.New("超出存储配额限制")
}
return nil
}
// assessStructureChangeRisk 评估结构变更风险级别
func assessStructureChangeRisk(changes []*pb.StructureChange) (RiskLevel, error) {
// 根据变更类型、影响范围等因素评估风险级别
riskLevel := RiskLevelLow
for _, change := range changes {
switch change.ChangeType {
case pb.ChangeType_DROP_COLUMN:
// 删除列操作风险较高
riskLevel = max(riskLevel, RiskLevelHigh)
case pb.ChangeType_MODIFY_COLUMN_TYPE:
// 修改列类型风险中等
riskLevel = max(riskLevel, RiskLevelMedium)
case pb.ChangeType_ADD_COLUMN:
// 添加列操作风险较低
riskLevel = max(riskLevel, RiskLevelLow)
}
}
return riskLevel, nil
}
9. 数据库创建错误处理和日志记录
9.1 数据库创建分层错误处理机制
数据库创建错误分类体系:
go
// 数据库创建错误类型定义
type DatabaseCreateErrorType int
const (
// 数据库创建业务错误
ErrDatabaseCreateBusiness DatabaseCreateErrorType = iota + 1000
ErrDatabaseNameExists
ErrDatabasePermissionDenied
ErrDatabaseCreateRateLimit
ErrDatabaseInvalidParameters
ErrDatabaseModelNotSupported
ErrDatabaseStorageQuotaExceeded
ErrDatabaseProcessingFailed
ErrDatabaseInvalidFileType
ErrDatabaseFileSizeExceeded
ErrDatabaseInvalidChunkSize
ErrDatabaseInvalidIconURI
ErrDatabaseInvalidSpaceID
ErrDatabaseDuplicateName
ErrDatabaseSpaceCreateFailed
// 数据库创建系统错误
ErrDatabaseCreateSystem DatabaseCreateErrorType = iota + 2000
ErrDatabaseDatabaseConnection
ErrDatabaseSearchTimeout
ErrDatabaseServiceUnavailable
ErrDatabaseCreateEventPublishFailed
ErrDatabaseIndexCreateFailed
ErrDatabaseTransactionRollbackFailed
ErrDatabaseStoreTimeout
ErrDatabaseIDGenerationFailed
ErrDatabaseModelServiceFailed
ErrDatabaseContentIndexFailed
// 数据库创建网络错误
ErrDatabaseCreateNetwork DatabaseCreateErrorType = iota + 3000
ErrDatabaseCreateRequestTimeout
ErrDatabaseCreateConnectionRefused
ErrDatabaseCreateServiceDown
ErrDatabaseCreateESConnectionFailed
ErrDatabaseDBConnectionFailed
ErrDatabaseModelAPITimeout
)
数据库创建错误处理流程:
- 捕获阶段:在数据库创建各层级捕获具体错误
- 包装阶段:添加数据库创建操作相关上下文信息和错误码
- 记录阶段:根据错误级别记录数据库创建操作日志
- 响应阶段:返回用户友好的数据库创建错误信息
- 回滚阶段:数据库创建失败时进行必要的数据回滚操作
- 向量处理:处理向量空间创建失败的错误
- 重试机制:对于可重试的创建错误提供重试建议
- 用户指导:为常见创建错误提供解决方案指导
9.2 数据库创建统一错误响应格式
go
// 数据库创建错误响应结构
type DatabaseCreateErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
TraceID string `json:"trace_id"`
KnowledgeID int64 `json:"knowledge_id,omitempty"`
Operation string `json:"operation"`
CanRetry bool `json:"can_retry"`
DocumentsProcessed int `json:"documents_processed,omitempty"`
DocumentsFailed int `json:"documents_failed,omitempty"`
ValidationErrors []string `json:"validation_errors,omitempty"`
SuggestedFix string `json:"suggested_fix,omitempty"`
FieldErrors map[string]string `json:"field_errors,omitempty"`
VectorSpaceStatus string `json:"vector_space_status,omitempty"`
EmbeddingModel string `json:"embedding_model,omitempty"`
}
// 数据库创建错误处理中间件
func DatabaseCreateErrorHandlerMiddleware() app.HandlerFunc {
return func(c context.Context, ctx *app.RequestContext) {
defer func() {
if err := recover(); err != nil {
traceID := ctx.GetString("trace_id")
userID := ctx.GetInt64("user_id")
spaceID := ctx.GetInt64("space_id")
logs.CtxErrorf(c, "Database creation panic recovered: %v, userID=%d, spaceID=%d, traceID=%s",
err, userID, spaceID, traceID)
ctx.JSON(500, DatabaseCreateErrorResponse{
Code: 5000,
Message: "数据库创建服务器内部错误",
TraceID: traceID,
Operation: "create_database",
CanRetry: true,
SuggestedFix: "请稍后重试,如果问题持续存在请联系技术支持",
})
}
}()
ctx.Next()
}
}
// 插件创建业务错误处理
func handlePluginCreateBusinessError(ctx *app.RequestContext, err error) {
traceID := ctx.GetString("trace_id")
var response PluginCreateErrorResponse
response.TraceID = traceID
response.Operation = "create_plugin"
switch {
case errors.Is(err, errno.ErrPluginInvalidParamCode):
response.Code = 400
response.Message = "插件参数无效"
response.CanRetry = false
response.SuggestedFix = "请检查插件名称、描述、服务器URL等参数是否正确"
case errors.Is(err, errno.ErrPluginPermissionCode):
response.Code = 403
response.Message = "无权限创建插件"
response.CanRetry = false
response.SuggestedFix = "请确保已登录且具有插件创建权限"
case errors.Is(err, errno.ErrPluginInvalidManifest):
response.Code = 400
response.Message = "插件清单格式无效"
response.CanRetry = false
response.SuggestedFix = "请检查插件清单文件格式是否符合规范"
case errors.Is(err, errno.ErrPluginInvalidOpenapi3Doc):
response.Code = 400
response.Message = "OpenAPI文档格式无效"
response.CanRetry = false
response.SuggestedFix = "请检查OpenAPI文档格式是否符合OpenAPI 3.0规范"
case errors.Is(err, errno.ErrPluginIDExist):
response.Code = 409
response.Message = "插件ID已存在"
response.CanRetry = false
response.SuggestedFix = "请使用不同的插件名称或检查是否已存在同名插件"
case errors.Is(err, errno.ErrPluginCreateRateLimit):
response.Code = 429
response.Message = "创建操作过于频繁,请稍后再试"
response.CanRetry = true
response.SuggestedFix = "请等待一段时间后重试"
case errors.Is(err, errno.ErrPluginStorageQuotaExceeded):
response.Code = 413
response.Message = "存储配额已满"
response.CanRetry = false
response.SuggestedFix = "请清理不需要的插件或升级存储配额"
case errors.Is(err, errno.ErrPluginServerURLNotAccessible):
response.Code = 400
response.Message = "插件服务器URL不可访问"
response.CanRetry = true
response.SuggestedFix = "请检查服务器URL是否正确且可访问"
default:
response.Code = 500
response.Message = "插件创建失败"
response.CanRetry = true
response.SuggestedFix = "请稍后重试,如果问题持续存在请联系技术支持"
}
ctx.JSON(response.Code, response)
}
// 插件创建系统错误处理
func handlePluginCreateSystemError(ctx *app.RequestContext, err error) {
traceID := ctx.GetString("trace_id")
var response PluginCreateErrorResponse
response.TraceID = traceID
response.Operation = "create_plugin"
switch {
case errors.Is(err, errno.ErrPluginDatabaseConnection):
response.Code = 500
response.Message = "插件数据库连接失败"
response.CanRetry = true
response.SuggestedFix = "数据库连接异常,请稍后重试"
case errors.Is(err, errno.ErrPluginElasticSearchTimeout):
response.Code = 500
response.Message = "插件索引操作超时"
response.CanRetry = true
response.SuggestedFix = "搜索服务响应超时,请稍后重试"
case errors.Is(err, errno.ErrPluginServiceUnavailable):
response.Code = 503
response.Message = "插件创建服务暂时不可用"
response.CanRetry = true
response.SuggestedFix = "服务正在维护中,请稍后重试"
case errors.Is(err, errno.ErrPluginCreateEventPublishFailed):
response.Code = 500
response.Message = "插件创建事件发布失败"
response.CanRetry = true
response.SuggestedFix = "事件发布异常,插件已创建但可能影响搜索,请稍后重试"
case errors.Is(err, errno.ErrPluginIndexCreateFailed):
response.Code = 500
response.Message = "插件索引创建失败"
response.CanRetry = true
response.SuggestedFix = "搜索索引创建失败,插件已创建但可能无法搜索到"
case errors.Is(err, errno.ErrPluginTransactionRollbackFailed):
response.Code = 500
response.Message = "插件创建事务回滚失败"
response.CanRetry = false
response.SuggestedFix = "数据一致性异常,请联系技术支持"
case errors.Is(err, errno.ErrPluginIDGenerationFailed):
response.Code = 500
response.Message = "插件ID生成失败"
response.CanRetry = true
response.SuggestedFix = "ID生成服务异常,请稍后重试"
default:
response.Code = 5000
response.Message = "插件创建失败"
response.Details = "服务器内部错误,请稍后重试"
response.CanRetry = true
response.SuggestedFix = "系统内部错误,请稍后重试或联系技术支持"
}
ctx.JSON(response.Code, response)
}
9.3 数据库创建日志记录策略
数据库创建日志级别定义:
- DEBUG:数据库创建详细调试信息,包括参数值、向量处理过程、文档分块详情
- INFO:数据库创建关键业务流程信息,如创建开始、参数验证、数据插入、向量空间创建
- WARN:数据库创建潜在问题警告,如存储配额警告、文档处理警告、向量生成警告
- ERROR:数据库创建错误信息,包括创建失败、权限错误、向量空间创建失败
- FATAL:数据库创建严重错误,可能导致数据不一致或向量空间损坏
数据库创建结构化日志格式:
go
// 数据库创建日志记录示例
func (s *KnowledgeApplicationService) CreateKnowledge(ctx context.Context, req *knowledgeAPI.CreateDatasetRequest) (*knowledgeAPI.CreateDatasetResponse, error) {
traceID := generateTraceID()
ctx = context.WithValue(ctx, "trace_id", traceID)
userID := ctxutil.GetUIDFromCtx(ctx)
// 记录数据库创建开始
logs.CtxInfof(ctx, "CreateKnowledge started, userID=%d, knowledgeName=%s, spaceID=%d, embeddingModel=%s, traceID=%s",
userID, req.GetName(), req.GetSpaceID(), req.GetEmbeddingModel(), traceID)
startTime := time.Now()
defer func() {
duration := time.Since(startTime)
logs.CtxInfof(ctx, "CreateKnowledge completed, duration=%dms, traceID=%s",
duration.Milliseconds(), traceID)
}()
// 记录关键步骤
logs.CtxInfof(ctx, "Validating knowledge create parameters, knowledgeName=%s, embeddingModel=%s, chunkSize=%d, traceID=%s",
req.GetName(), req.GetEmbeddingModel(), req.GetChunkSize(), traceID)
// 权限验证日志
logs.CtxInfof(ctx, "Validating knowledge create permission, userID=%d, spaceID=%d, traceID=%s",
userID, req.GetSpaceID(), traceID)
// 存储配额检查日志
logs.CtxInfof(ctx, "Checking storage quota, userID=%d, traceID=%s", userID, traceID)
// 向量空间创建日志
logs.CtxInfof(ctx, "Creating vector space, embeddingModel=%s, dimensions=%d, traceID=%s",
req.GetEmbeddingModel(), getModelDimensions(req.GetEmbeddingModel()), traceID)
// 数据库创建操作日志
logs.CtxInfof(ctx, "Creating knowledge in database, knowledgeName=%s, traceID=%s",
req.GetName(), traceID)
// ElasticSearch索引创建日志
logs.CtxInfof(ctx, "Creating ElasticSearch index, knowledgeID=%d, traceID=%s",
knowledgeID, traceID)
// 事件发布日志
logs.CtxInfof(ctx, "Publishing knowledge create event, knowledgeID=%d, traceID=%s",
knowledgeID, traceID)
return resp, nil
}
// 数据库创建操作审计日志
func (s *KnowledgeApplicationService) logDatabaseCreateAudit(ctx context.Context, operation string, knowledgeID int64, details map[string]interface{}) {
userID := ctx.Value("user_id").(int64)
spaceID := ctx.Value("space_id").(int64)
traceID := ctx.Value("trace_id").(string)
auditLog := map[string]interface{}{
"operation": operation,
"knowledge_id": knowledgeID,
"user_id": userID,
"space_id": spaceID,
"trace_id": traceID,
"timestamp": time.Now().Unix(),
"details": details,
"knowledge_name": details["knowledge_name"],
"embedding_model": details["embedding_model"],
"chunk_size": details["chunk_size"],
"chunk_overlap": details["chunk_overlap"],
"vector_space_id": details["vector_space_id"],
"storage_used": details["storage_used"],
}
logs.CtxInfof(ctx, "Knowledge create audit log: %+v", auditLog)
}
// 文档处理日志记录
func (s *KnowledgeApplicationService) logDocumentProcessing(ctx context.Context, knowledgeID int64, documentID int64, operation string, details map[string]interface{}) {
traceID := ctx.Value("trace_id").(string)
docLog := map[string]interface{}{
"operation": operation,
"knowledge_id": knowledgeID,
"document_id": documentID,
"trace_id": traceID,
"timestamp": time.Now().Unix(),
"details": details,
"file_name": details["file_name"],
"file_size": details["file_size"],
"chunk_count": details["chunk_count"],
"vector_count": details["vector_count"],
"processing_time": details["processing_time"],
}
logs.CtxInfof(ctx, "Document processing log: %+v", docLog)
}
// 向量空间操作日志
func (s *KnowledgeApplicationService) logVectorSpaceOperation(ctx context.Context, operation string, vectorSpaceID string, details map[string]interface{}) {
traceID := ctx.Value("trace_id").(string)
vectorLog := map[string]interface{}{
"operation": operation,
"vector_space_id": vectorSpaceID,
"trace_id": traceID,
"timestamp": time.Now().Unix(),
"details": details,
"embedding_model": details["embedding_model"],
"dimensions": details["dimensions"],
"vector_count": details["vector_count"],
"index_type": details["index_type"],
}
logs.CtxInfof(ctx, "Vector space operation log: %+v", vectorLog)
}
数据库创建日志内容规范:
- 请求日志:记录用户ID、工作空间ID、知识库名称、嵌入模型、分块策略、TraceID
- 业务日志:记录数据库创建步骤、参数验证结果、权限验证结果、向量空间创建过程
- 性能日志:记录创建接口响应时间、数据库插入时间、向量空间创建时间、文档处理时间
- 错误日志:记录创建错误堆栈、知识库相关上下文信息、向量处理失败原因
- 审计日志:记录知识库的创建操作、创建参数、创建结果、关联的文档和向量信息
- 安全日志:记录创建频率、权限验证、存储配额检查、可疑创建行为
- 文档处理日志:记录文档上传、分块处理、向量生成、索引创建等详细过程
- 向量空间日志:记录向量空间创建、配置、索引构建、查询性能等信息
9.4 数据库创建监控和告警
数据库创建关键指标监控:
- 创建性能:数据库创建响应时间、创建成功率、创建QPS、创建吞吐量
- 资源使用:数据库连接数、向量空间创建延迟、内存使用率、文档处理队列长度
- 业务指标:数据库创建成功率、创建频率分布、不同嵌入模型使用比例、用户创建活跃度
- 安全指标:权限验证通过率、恶意创建尝试次数、创建频率限制触发次数、存储配额检查失败率
- 质量指标:向量空间创建成功率、文档处理成功率、嵌入模型响应率、索引创建成功率
- 存储指标:存储使用量、向量数量、文档数量、索引大小、存储增长率
- 向量处理指标:向量生成延迟、向量维度分布、嵌入模型调用次数、向量相似度计算性能
数据库创建告警策略:
- 创建失败率告警:当数据库创建失败率超过3%时触发告警
- 性能告警:当数据库创建响应时间超过10秒时触发告警
- 资源告警:当数据库连接数超过80%或向量数据库连接异常时触发告警
- 安全告警:当检测到异常创建行为或存储配额滥用时立即触发告警
- 数据一致性告警:当MySQL、ES和向量数据库创建状态不一致时触发告警
- 配额告警:当用户存储使用量超过90%时触发告警
- 向量服务告警:当嵌入模型服务不可用或响应超时时触发告警
- 文档处理告警:当文档处理队列积压超过阈值时触发告警
go
// 数据库创建监控指标收集
type DatabaseCreateMetrics struct {
CreateSuccessCount int64 // 创建成功次数
CreateFailureCount int64 // 创建失败次数
CreateLatency time.Duration // 创建延迟
PermissionDeniedCount int64 // 权限拒绝次数
RateLimitCount int64 // 频率限制次数
ParameterValidationFailCount int64 // 参数验证失败次数
VectorSpaceCreateLatency time.Duration // 向量空间创建延迟
VectorSpaceCreateFailCount int64 // 向量空间创建失败次数
DocumentProcessingLatency time.Duration // 文档处理延迟
EmbeddingGenerationLatency time.Duration // 嵌入生成延迟
EmbeddingModelFailCount int64 // 嵌入模型调用失败次数
StorageQuotaExceededCount int64 // 存储配额超限次数
IndexCreateLatency time.Duration // 索引创建延迟
IndexCreateFailCount int64 // 索引创建失败次数
EventPublishLatency time.Duration // 事件发布延迟
DatabaseInsertLatency time.Duration // 数据库插入延迟
VectorDatabaseLatency time.Duration // 向量数据库操作延迟
TotalStorageUsed int64 // 总存储使用量
TotalVectorCount int64 // 总向量数量
TotalDocumentCount int64 // 总文档数量
}
// 数据库创建监控指标上报
func (s *DatabaseApplicationService) reportCreateMetrics(ctx context.Context, operation string, startTime time.Time, databaseID int64, req *databaseAPI.CreateDatabaseRequest, err error) {
latency := time.Since(startTime)
if err != nil {
metrics.CreateFailureCount++
// 根据错误类型分类统计
switch {
case errors.Is(err, errno.ErrDatabasePermissionCode):
metrics.PermissionDeniedCount++
case errors.Is(err, errno.ErrDatabaseCreateRateLimitCode):
metrics.RateLimitCount++
case errors.Is(err, errno.ErrDatabaseInvalidParamCode):
metrics.ParameterValidationFailCount++
case errors.Is(err, errno.ErrDatabaseStorageQuotaExceededCode):
metrics.StorageQuotaExceededCount++
case errors.Is(err, errno.ErrDatabaseSpaceCreateFailedCode):
metrics.VectorSpaceCreateFailCount++
case errors.Is(err, errno.ErrDatabaseEmbeddingModelFailedCode):
metrics.EmbeddingModelFailCount++
case errors.Is(err, errno.ErrDatabaseIndexCreateFailedCode):
metrics.IndexCreateFailCount++
}
logs.CtxErrorf(ctx, "Database %s failed, databaseName=%s, spaceID=%d, embeddingModel=%s, error=%v, latency=%dms",
operation, req.GetName(), req.GetSpaceID(), req.GetEmbeddingModel(), err, latency.Milliseconds())
} else {
metrics.CreateSuccessCount++
metrics.CreateLatency = latency
// 记录数据库类型统计
embeddingModel := req.GetEmbeddingModel()
chunkSize := req.GetChunkSize()
logs.CtxInfof(ctx, "Database %s succeeded, databaseID=%d, databaseName=%s, embeddingModel=%s, chunkSize=%d, latency=%dms",
operation, databaseID, req.GetName(), embeddingModel, chunkSize, latency.Milliseconds())
}
// 上报到监控系统
s.metricsReporter.Report(ctx, "database_create", map[string]interface{}{
"operation": operation,
"database_id": databaseID,
"database_name": req.GetName(),
"embedding_model": req.GetEmbeddingModel(),
"chunk_size": req.GetChunkSize(),
"chunk_overlap": req.GetChunkOverlap(),
"space_id": req.GetSpaceID(),
"success": err == nil,
"latency_ms": latency.Milliseconds(),
"error_type": getDatabaseCreateErrorType(err),
"vector_dimensions": getModelDimensions(req.GetEmbeddingModel()),
"storage_used": getStorageUsed(ctx, req.GetSpaceID()),
})
}
// 获取数据库创建错误类型
func getDatabaseCreateErrorType(err error) string {
if err == nil {
return "none"
}
// 基于数据库错误码定义
switch {
case errors.Is(err, errno.ErrDatabasePermissionCode):
return "permission_denied"
case errors.Is(err, errno.ErrDatabaseNameExistsCode):
return "database_exists"
case errors.Is(err, errno.ErrDatabaseInvalidParamCode):
return "invalid_parameters"
case errors.Is(err, errno.ErrDatabaseStorageQuotaExceededCode):
return "storage_quota_exceeded"
case errors.Is(err, errno.ErrDatabaseSpaceCreateFailedCode):
return "vector_space_create_failed"
case errors.Is(err, errno.ErrDatabaseEmbeddingModelFailedCode):
return "embedding_model_failed"
case errors.Is(err, errno.ErrDatabaseIndexCreateFailedCode):
return "index_create_failed"
case errors.Is(err, errno.ErrDatabaseProcessingFailedCode):
return "document_processing_failed"
case errors.Is(err, errno.ErrDatabaseVectorDatabaseTimeoutCode):
return "vector_database_timeout"
case errors.Is(err, errno.ErrDatabaseCreateRateLimitCode):
return "rate_limit_exceeded"
default:
return "system_error"
}
}
// 数据库创建告警检查
func (s *DatabaseApplicationService) checkCreateAlerts(ctx context.Context, metrics *DatabaseCreateMetrics) {
// 创建失败率告警
totalCreates := metrics.CreateSuccessCount + metrics.CreateFailureCount
if totalCreates > 100 {
failureRate := float64(metrics.CreateFailureCount) / float64(totalCreates)
if failureRate > 0.03 { // 3%
s.alertManager.SendAlert(ctx, &Alert{
Level: "warning",
Type: "database_create_failure_rate",
Message: fmt.Sprintf("数据库创建失败率过高: %.2f%%", failureRate*100),
Metrics: map[string]interface{}{
"failure_rate": failureRate,
"total_creates": totalCreates,
},
})
}
}
// 性能告警
if metrics.CreateLatency > 10*time.Second {
s.alertManager.SendAlert(ctx, &Alert{
Level: "warning",
Type: "database_create_latency",
Message: fmt.Sprintf("数据库创建延迟过高: %dms", metrics.CreateLatency.Milliseconds()),
Metrics: map[string]interface{}{
"latency_ms": metrics.CreateLatency.Milliseconds(),
},
})
}
// 存储配额告警
if metrics.StorageQuotaExceededCount > 10 {
s.alertManager.SendAlert(ctx, &Alert{
Level: "critical",
Type: "database_storage_quota_exceeded",
Message: fmt.Sprintf("存储配额超限次数过多: %d", metrics.StorageQuotaExceededCount),
Metrics: map[string]interface{}{
"quota_exceeded_count": metrics.StorageQuotaExceededCount,
},
})
}
// 向量空间创建失败告警
if metrics.VectorSpaceCreateFailCount > 5 {
s.alertManager.SendAlert(ctx, &Alert{
Level: "critical",
Type: "database_vector_space_create_failed",
Message: fmt.Sprintf("向量空间创建失败次数过多: %d", metrics.VectorSpaceCreateFailCount),
Metrics: map[string]interface{}{
"vector_space_fail_count": metrics.VectorSpaceCreateFailCount,
},
})
}
// 嵌入模型失败告警
if metrics.EmbeddingModelFailCount > 20 {
s.alertManager.SendAlert(ctx, &Alert{
Level: "warning",
Type: "database_embedding_model_failed",
Message: fmt.Sprintf("嵌入模型调用失败次数过多: %d", metrics.EmbeddingModelFailCount),
Metrics: map[string]interface{}{
"embedding_fail_count": metrics.EmbeddingModelFailCount,
},
})
}
}