블로그 Engineering

One In-App Notification, Many Notifiers: A Merged Fix for Halo

영문 기사. Halo created a duplicate in-app notification for every enabled notifier. The merged fix still sends through each notifier but creates one in-app message.

Javahalo-dev/haloProject ReactornotificationsCMS

A reactive-pipeline fix in one of the most-starred projects in this series.

짧은 답변

In Halo, a subscriber with several notifiers enabled, such as email plus a plugin notifier, got one duplicate in-app notification per notifier. The fix collects the prepared notification elements, sends one through each notifier, and creates a single in-app notification. It was merged as PR #10264 and closed issue #7747.

The problem

Halo is an open-source website builder and CMS written in Java. Issue #7747, reported on Halo 2.21.7, showed duplicate notification messages once more than one notifier was installed.

Root cause

DefaultNotificationCenter.dispatchNotification() ran a reactive pipeline per notifier, and each branch both sent the notification and created an in-app Notification. With two notifiers, the user got two in-app messages for the same event.

The fix

.flatMap(descriptor -> prepareNotificationElement(subscriber, reason, descriptor))
.collectList()
.filter(elements -> !elements.isEmpty())
.flatMap(elements -> {
    var dispatchMono = Flux.fromIterable(elements)
            .flatMap(this::sendNotification)
            .then();
    if (subscriber.isAnonymous()) {
        return dispatchMono;
    }
    // create notification for user
    var innerNotificationMono = createNotification(elements.get(0));
    return Mono.when(dispatchMono, innerNotificationMono);
})

The change also fixes a variable name typo (innerNofificationMono). A new test sets up two notifiers and verifies two sends and one in-app notification:

verify(spyNotificationCenter, times(2)).sendNotification(any());
verify(spyNotificationCenter, times(1)).createNotification(any());

By the numbers

  • Repository: halo-dev/halo, 40,107 stars (Nov 3, 2026), Java, GPL-3.0
  • Files changed: 2 (1 source file, 1 test file)
  • Lines: +42 / -4
  • Commits: 1
  • Opened: Aug 21, 2026. Merged: Aug 25, 2026. Time to merge: 3 days, 20 hours
  • Review: approved and merged by a maintainer; CLA signed; quality gate passed

How the review went

The PR included a release note: "Fix duplicate in-app notifications when multiple notifiers are enabled." Automated checks reported the quality gate passed and 87.5% patch coverage. A maintainer approved and merged it without requesting changes.

FAQ

Do external notifiers still each receive the message?

Yes. Every enabled notifier still gets a send. Only the in-app notification is deduplicated.

Where can I read more?

See the pull request and the original issue.

목표

Reactive pipelines make it easy to do one thing per element when you meant to do it once. Collecting first, then splitting the side effects, states the intent in the code.

루프 실행

프로덕션 신호를 리뷰된 수정으로 바꾸세요.

무료 체험으로 Prilog가 실제 인시던트를 코드 수준 Pull Request로 연결하는 방식을 확인하세요.