fix: Limit on demand recompute when no recsUnverified
39604ad parent: 4fa381d modified
internal/cluster/precompute.go +7 -14 | @@ -79,9 +79,9 @@ func (e *Engine) precomputeForUser(ctx context.Context, userDID string) error { | ||
| 79 | 79 | } |
| 80 | 80 | } |
| 81 | 81 | |
| 82 | - e.storePrecomputed(ctx, userDID, "feed", mustJSON(feedRecs)) | |
| 83 | - e.storePrecomputed(ctx, userDID, "article", mustJSON(articleRecs)) | |
| 84 | - e.storePrecomputed(ctx, userDID, "person", mustJSON(peopleRecs)) | |
| 82 | + e.storeRecs(ctx, userDID, "feed", feedRecs) | |
| 83 | + e.storeRecs(ctx, userDID, "article", articleRecs) | |
| 84 | + e.storeRecs(ctx, userDID, "person", peopleRecs) | |
| 85 | 85 | |
| 86 | 86 | e.logger.Debug("precomputed recommendations for user", |
| 87 | 87 | "did", userDID, |
| @@ -128,9 +128,10 @@ func (e *Engine) getPrecomputed(ctx context.Context, userDID, recType string) (s | ||
| 128 | 128 | return data, true |
| 129 | 129 | } |
| 130 | 130 | |
| 131 | -func (e *Engine) storePrecomputed(ctx context.Context, userDID, recType string, data string) { | |
| 132 | - if data == "null" { | |
| 133 | - return | |
| 131 | +func (e *Engine) storeRecs(ctx context.Context, userDID, recType string, v any) { | |
| 132 | + data := "[]" | |
| 133 | + if b, err := json.Marshal(v); err == nil { | |
| 134 | + data = string(b) | |
| 134 | 135 | } |
| 135 | 136 | _, _ = e.db.ExecContext(ctx, ` |
| 136 | 137 | INSERT INTO recs.precomputed_recommendations (user_did, rec_type, data, computed_at) |
| @@ -138,11 +139,3 @@ func (e *Engine) storePrecomputed(ctx context.Context, userDID, recType string, | ||
| 138 | 139 | ON CONFLICT(user_did, rec_type) DO UPDATE SET data = excluded.data, computed_at = excluded.computed_at |
| 139 | 140 | `, userDID, recType, data) |
| 140 | 141 | } |
| 141 | - | |
| 142 | -func mustJSON(v any) string { | |
| 143 | - b, err := json.Marshal(v) | |
| 144 | - if err != nil { | |
| 145 | - return "null" | |
| 146 | - } | |
| 147 | - return string(b) | |
| 148 | -} | |
| @@ -79,9 +79,9 @@ func (e *Engine) precomputeForUser(ctx context.Context, userDID string) error { | |||
| 79 | } | 79 | } |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | - e.storePrecomputed(ctx, userDID, "feed", mustJSON(feedRecs)) | 82 | + e.storeRecs(ctx, userDID, "feed", feedRecs) |
| 83 | - e.storePrecomputed(ctx, userDID, "article", mustJSON(articleRecs)) | 83 | + e.storeRecs(ctx, userDID, "article", articleRecs) |
| 84 | - e.storePrecomputed(ctx, userDID, "person", mustJSON(peopleRecs)) | 84 | + e.storeRecs(ctx, userDID, "person", peopleRecs) |
| 85 | 85 | ||
| 86 | e.logger.Debug("precomputed recommendations for user", | 86 | e.logger.Debug("precomputed recommendations for user", |
| 87 | "did", userDID, | 87 | "did", userDID, |
| @@ -128,9 +128,10 @@ func (e *Engine) getPrecomputed(ctx context.Context, userDID, recType string) (s | |||
| 128 | return data, true | 128 | return data, true |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | -func (e *Engine) storePrecomputed(ctx context.Context, userDID, recType string, data string) { | 131 | +func (e *Engine) storeRecs(ctx context.Context, userDID, recType string, v any) { |
| 132 | - if data == "null" { | 132 | + data := "[]" |
| 133 | - return | 133 | + if b, err := json.Marshal(v); err == nil { |
| 134 | + data = string(b) | ||
| 134 | } | 135 | } |
| 135 | _, _ = e.db.ExecContext(ctx, ` | 136 | _, _ = e.db.ExecContext(ctx, ` |
| 136 | INSERT INTO recs.precomputed_recommendations (user_did, rec_type, data, computed_at) | 137 | INSERT INTO recs.precomputed_recommendations (user_did, rec_type, data, computed_at) |
| @@ -138,11 +139,3 @@ func (e *Engine) storePrecomputed(ctx context.Context, userDID, recType string, | |||
| 138 | ON CONFLICT(user_did, rec_type) DO UPDATE SET data = excluded.data, computed_at = excluded.computed_at | 139 | ON CONFLICT(user_did, rec_type) DO UPDATE SET data = excluded.data, computed_at = excluded.computed_at |
| 139 | `, userDID, recType, data) | 140 | `, userDID, recType, data) |
| 140 | } | 141 | } |
| 141 | - | ||
| 142 | -func mustJSON(v any) string { | ||
| 143 | - b, err := json.Marshal(v) | ||
| 144 | - if err != nil { | ||
| 145 | - return "null" | ||
| 146 | - } | ||
| 147 | - return string(b) | ||
| 148 | -} | ||
modified
internal/cluster/scoring.go +4 -4 | @@ -81,7 +81,7 @@ func (e *Engine) GetFeedRecommendations(ctx context.Context, userDID string, lim | ||
| 81 | 81 | if err == nil && len(recs) > 0 { |
| 82 | 82 | normalizeFeedScores(recs) |
| 83 | 83 | result := ApplyDiversity(recs, limit) |
| 84 | - e.storePrecomputed(ctx, userDID, "feed", mustJSON(result)) | |
| 84 | + e.storeRecs(ctx, userDID, "feed", result) | |
| 85 | 85 | e.logger.Info("feed recommendations computed (cold-start)", "did", userDID, "count", len(result), "duration", time.Since(start)) |
| 86 | 86 | return result, nil |
| 87 | 87 | } |
| @@ -94,7 +94,7 @@ func (e *Engine) GetFeedRecommendations(ctx context.Context, userDID string, lim | ||
| 94 | 94 | |
| 95 | 95 | normalizeFeedScores(recs) |
| 96 | 96 | result := ApplyDiversity(recs, limit) |
| 97 | - e.storePrecomputed(ctx, userDID, "feed", mustJSON(result)) | |
| 97 | + e.storeRecs(ctx, userDID, "feed", result) | |
| 98 | 98 | e.logger.Info("feed recommendations computed (on-demand)", "did", userDID, "count", len(result), "duration", time.Since(start)) |
| 99 | 99 | return result, nil |
| 100 | 100 | } |
| @@ -131,7 +131,7 @@ func (e *Engine) GetPeopleRecommendations(ctx context.Context, userDID string, l | ||
| 131 | 131 | recs = append(recs, outNet...) |
| 132 | 132 | |
| 133 | 133 | normalizePersonScores(recs) |
| 134 | - e.storePrecomputed(ctx, userDID, "person", mustJSON(recs)) | |
| 134 | + e.storeRecs(ctx, userDID, "person", recs) | |
| 135 | 135 | e.logger.Info("people recommendations computed (on-demand)", "did", userDID, "count", len(recs), "duration", time.Since(start)) |
| 136 | 136 | return recs, nil |
| 137 | 137 | } |
| @@ -156,7 +156,7 @@ func (e *Engine) GetArticleRecommendations(ctx context.Context, userDID string, | ||
| 156 | 156 | return nil, err |
| 157 | 157 | } |
| 158 | 158 | normalizeArticleScores(recs) |
| 159 | - e.storePrecomputed(ctx, userDID, "article", mustJSON(recs)) | |
| 159 | + e.storeRecs(ctx, userDID, "article", recs) | |
| 160 | 160 | e.logger.Info("article recommendations computed (on-demand)", "did", userDID, "count", len(recs), "duration", time.Since(start)) |
| 161 | 161 | return recs, nil |
| 162 | 162 | } |
| @@ -81,7 +81,7 @@ func (e *Engine) GetFeedRecommendations(ctx context.Context, userDID string, lim | |||
| 81 | if err == nil && len(recs) > 0 { | 81 | if err == nil && len(recs) > 0 { |
| 82 | normalizeFeedScores(recs) | 82 | normalizeFeedScores(recs) |
| 83 | result := ApplyDiversity(recs, limit) | 83 | result := ApplyDiversity(recs, limit) |
| 84 | - e.storePrecomputed(ctx, userDID, "feed", mustJSON(result)) | 84 | + e.storeRecs(ctx, userDID, "feed", result) |
| 85 | e.logger.Info("feed recommendations computed (cold-start)", "did", userDID, "count", len(result), "duration", time.Since(start)) | 85 | e.logger.Info("feed recommendations computed (cold-start)", "did", userDID, "count", len(result), "duration", time.Since(start)) |
| 86 | return result, nil | 86 | return result, nil |
| 87 | } | 87 | } |
| @@ -94,7 +94,7 @@ func (e *Engine) GetFeedRecommendations(ctx context.Context, userDID string, lim | |||
| 94 | 94 | ||
| 95 | normalizeFeedScores(recs) | 95 | normalizeFeedScores(recs) |
| 96 | result := ApplyDiversity(recs, limit) | 96 | result := ApplyDiversity(recs, limit) |
| 97 | - e.storePrecomputed(ctx, userDID, "feed", mustJSON(result)) | 97 | + e.storeRecs(ctx, userDID, "feed", result) |
| 98 | e.logger.Info("feed recommendations computed (on-demand)", "did", userDID, "count", len(result), "duration", time.Since(start)) | 98 | e.logger.Info("feed recommendations computed (on-demand)", "did", userDID, "count", len(result), "duration", time.Since(start)) |
| 99 | return result, nil | 99 | return result, nil |
| 100 | } | 100 | } |
| @@ -131,7 +131,7 @@ func (e *Engine) GetPeopleRecommendations(ctx context.Context, userDID string, l | |||
| 131 | recs = append(recs, outNet...) | 131 | recs = append(recs, outNet...) |
| 132 | 132 | ||
| 133 | normalizePersonScores(recs) | 133 | normalizePersonScores(recs) |
| 134 | - e.storePrecomputed(ctx, userDID, "person", mustJSON(recs)) | 134 | + e.storeRecs(ctx, userDID, "person", recs) |
| 135 | e.logger.Info("people recommendations computed (on-demand)", "did", userDID, "count", len(recs), "duration", time.Since(start)) | 135 | e.logger.Info("people recommendations computed (on-demand)", "did", userDID, "count", len(recs), "duration", time.Since(start)) |
| 136 | return recs, nil | 136 | return recs, nil |
| 137 | } | 137 | } |
| @@ -156,7 +156,7 @@ func (e *Engine) GetArticleRecommendations(ctx context.Context, userDID string, | |||
| 156 | return nil, err | 156 | return nil, err |
| 157 | } | 157 | } |
| 158 | normalizeArticleScores(recs) | 158 | normalizeArticleScores(recs) |
| 159 | - e.storePrecomputed(ctx, userDID, "article", mustJSON(recs)) | 159 | + e.storeRecs(ctx, userDID, "article", recs) |
| 160 | e.logger.Info("article recommendations computed (on-demand)", "did", userDID, "count", len(recs), "duration", time.Since(start)) | 160 | e.logger.Info("article recommendations computed (on-demand)", "did", userDID, "count", len(recs), "duration", time.Since(start)) |
| 161 | return recs, nil | 161 | return recs, nil |
| 162 | } | 162 | } |