fix: Prevent promotion filtering to exceed psql limits (#13540)

* fix(): Prevent promotion filtering to exceed psql limits

* Create sour-rockets-grin.md
This commit is contained in:
Adrien de Peretti
2025-09-18 17:50:10 +02:00
committed by GitHub
parent aa7ea4d9a6
commit 4736c58da5
4 changed files with 43 additions and 6 deletions

View File

@@ -0,0 +1,5 @@
---
"@medusajs/promotion": patch
---
fix(): Prevent promotion filtering to exceed psql limits

View File

@@ -353,7 +353,7 @@ moduleIntegrationTestRunner({
value: 100,
target_rules: [
{
attribute: "product.id",
attribute: "items.product.id",
operator: "eq",
values: ["prod_tshirt0"], // Only applies to product 0
},

View File

@@ -466,7 +466,10 @@ export default class PromotionModuleService
if (!preventAutoPromotions) {
const rulePrefilteringFilters =
buildPromotionRuleQueryFilterFromContext(applicationContext)
await buildPromotionRuleQueryFilterFromContext(
applicationContext,
sharedContext
)
let prefilteredAutomaticPromotionIds: string[] = []

View File

@@ -2,11 +2,12 @@ import {
ComputeActionContext,
ComputeActionItemLine,
ComputeActionShippingLine,
Context,
DAL,
PromotionTypes,
} from "@medusajs/framework/types"
import { flattenObjectToKeyValuePairs } from "@medusajs/framework/utils"
import { raw } from "@mikro-orm/postgresql"
import { raw, SqlEntityManager } from "@mikro-orm/postgresql"
/**
* Builds a query filter for promotion rules based on the context.
@@ -17,9 +18,10 @@ import { raw } from "@mikro-orm/postgresql"
* @param context
* @returns
*/
export function buildPromotionRuleQueryFilterFromContext(
context: PromotionTypes.ComputeActionContext
): DAL.FilterQuery<any> | null {
export async function buildPromotionRuleQueryFilterFromContext(
context: PromotionTypes.ComputeActionContext,
sharedContext: Context
): Promise<DAL.FilterQuery<any> | null> {
const {
items = [],
shipping_methods: shippingMethods = [],
@@ -67,6 +69,33 @@ export function buildPromotionRuleQueryFilterFromContext(
})
})
// count the number of attributes in the map
const numberOfAttributes = attributeValueMap.size
if (numberOfAttributes > 10) {
const manager = (sharedContext.transactionManager ??
sharedContext.manager) as SqlEntityManager
const knex = manager.getKnex()
const { rows } = await knex.raw(
`
SELECT DISTINCT attribute
FROM promotion_rule
WHERE deleted_at IS NULL
`
)
const dbAvailableAttributes = new Set(
rows.map(({ attribute }) => attribute)
)
// update the attribute in the map to remove the one that are not in the db
attributeValueMap.forEach((valueSet, attribute) => {
if (!dbAvailableAttributes.has(attribute)) {
attributeValueMap.delete(attribute)
}
})
}
// Build conditions for a NOT EXISTS subquery to exclude promotions with unsatisfiable rules
const sqlConditions: string[] = []