Skip to content

Two generates:/posts: rules sharing a target back-reference silently collide - detect it at parse time #6813

Description

@delchev

Summary

Two or more event-driven generates: / posts: rules sharing a target entity AND a back-reference relation silently collide: the at-most-once guard is scoped per (target, back-reference relation), not per rule, so the first rule to fire claims the source permanently and every other rule no-ops forever. Nothing flags this at parse time - it parses, generates and compiles cleanly.

Found by static source review against origin/master (1dc87fb505).

Where

  • events/Generate.java.template:91-102 - the guard (inside #if($hasEvent)): findAll(eq(backRefProperty, sourceId)), return existing if non-empty. The query is indifferent to WHICH rule created the row and to its data.
  • events/Posts.java.template:53-58 - the identical guard (inside #if($backRef != "")). Note: a posts: entry whose glue yields an empty backRef gets NO idempotency guard at all - the opposite edge of the same coin.
  • GlueIntentGenerator.putGeneratesEvent derives backRefProperty from the map: entry copying the source pk - so two rules with map: { Fine: id } share one guard.

Example

generates:
  - name: log-identification-failed
    from: Fine
    to: FineLog
    event: { onTransition: Fine, when: "Status == UNRESOLVED" }
    map: { Fine: id }
  - name: log-declaration-created
    from: Fine
    to: FineLog
    event: { onTransition: Fine, when: "Status == DECLARED" }
    map: { Fine: id }              # SAME back-reference

Rule 1 writes a row on failure; rule 2 later finds that row via the shared relation and returns it - it never writes anything, for this or any Fine that already has a row.

Relationship to #6800

#6800 (mode: once|append) gives the accumulating-target case a correct option and removes the COMMON collision case - but it mitigates rather than fixes this: two mode: once rules sharing a target + back-reference still silently no-op with no diagnostic.

Proposed fix

Parse-time detection (cheap, high value): error - or at minimum a generate warning - when two generates:/posts: rules share a target entity and a back-reference relation. Both are static in the model; this converts a silent runtime defect into an authoring-time message.

Deeper option: scope the guard per rule (match on a rule-owned discriminator - the rule name, or a declared kind column) so rules through one relation no longer share one guard while idempotence under redelivery is preserved.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions