Skip to content

Use Noticed for notifications on GPX imports#6939

Open
pablobm wants to merge 2 commits intoopenstreetmap:masterfrom
pablobm:convert-mailers-to-notifiers
Open

Use Noticed for notifications on GPX imports#6939
pablobm wants to merge 2 commits intoopenstreetmap:masterfrom
pablobm:convert-mailers-to-notifiers

Conversation

@pablobm
Copy link
Copy Markdown
Contributor

@pablobm pablobm commented Mar 25, 2026

Following #6837, and sibling to #6933, this converts some more code to use Noticed instead of using mailers directly.

The success notification was the simpler of the two. I only needed to adapt the mailer to use the recipient explicitly given in the params, as opposed to extracting it from the trace. This was raised by @tomhughes during review.

The failure notification is a bit more involved. First the two easy changes:

  • Same as with success, the recipient is given explicitly instead of extracted from the trace.
  • I changed the test because a) it depended on the trace record being available and b) I don't like that sort of negative assertion it was using.

More importantly, there's the issue that the current code deletes the trace immediately after delivering the email synchronously. To work around this, the notifier will not receive a record, only the metadata as explicit arguments, and the recipient will be given explicitly in the deliver_later call.

In any case, and as mentioned by @gravitystorm, this raises the question of what to do when we implement on-site notifications and these refer to deleted records. This might involve creating a new model OnsiteNotification that doesn't require other records, instead of piggybacking on the existing Noticed::Notifications.

@gravitystorm
Copy link
Copy Markdown
Collaborator

Thoughts?

My initial reaction is that this is the classic ActiveJob model reference problem. Because the job stores a reference to the object, and then looks up the object attributes at some later time, it's not guaranteed that the object still exists when the job runs. This is that same problem on maximum scale, since it affects 100% of the jobs, instead of just a small enough percentage that everyone sweeps the problem under the carpet.

My usual approach for similar circumstances is to serialise the required attributes into the job. This means the job runs without needing to load the object. So something like:

- GpxImportFailureNotifier.with(:record => trace, :error => e.to_s).deliver_later
+ GpxImportFailureNotifier.with(:error => e.to_s, :trace_name => trace.name, :trace_description => trace.description, :trace_tags => ...etc...).deliver_later

However, I don't know if noticed is particularly happy without having a record to associate with. https://github.com/excid3/noticed?tab=readme-ov-file#handling-deleted-records only covers the case where you want to avoid sending notifications for deleted objects, but that's the opposite of what we want here.

This also makes me think about how we would handle this with on-site notifications. What if I upload 10 traces and come back to the website next weekend? How will the system know what to write in the site notifications?

@tomhughes
Copy link
Copy Markdown
Member

Maybe we should just stop deleting the records? We can set visible to false so they're not shown in listings, and delete the attachment, but keep the actual record?

If you really wanted you can have a cleanup job as part of the daily cleanup task I mentioned that removed any that were no longer referenced by a notification, once we have the notification cleanup we discussed before.

# frozen_string_literal: true

class GpxImportSuccessNotifier < ApplicationNotifier
recipients -> { record.user }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is controlling what user the notification is linked to, but it doesn't actually effect where the email goes because the gpx_success mailer determines the recipient itself. Is that going to be confusing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh, you are right. The logic in the mailer should change to use recipient. Done now.


trace.stub(:import, gpx) do
TraceImporterJob.perform_now(trace)
perform_enqueued_jobs do
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needed because the perform_now only runs the trace import job and we need to then run the event job before the mail is submitted now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. For completeness:

  • The notification jobs launched by TraceImporterJob are now async (this PR moves them from deliver to deliver_later).
  • The notification jobs in turn launch mailer jobs that are also async.

So the queue needs to be exhausted of the immediate jobs and those generated in turn.

@pablobm pablobm force-pushed the convert-mailers-to-notifiers branch 2 times, most recently from fa9fbc0 to 9f62bea Compare March 26, 2026 12:49
@pablobm
Copy link
Copy Markdown
Contributor Author

pablobm commented Mar 26, 2026

(EDIT 2026-04-07: comment superseded by my next one).

Good points, I should have stopped to think a bit more.

I had a look this morning, I see a couple of solutions:

Any preferences?

I have rewritten the PR description to reflect the latest changes, as well as answered the questions on specific changes.

@pablobm pablobm force-pushed the convert-mailers-to-notifiers branch 4 times, most recently from a944d11 to 6aeb440 Compare March 26, 2026 17:41
@pablobm pablobm force-pushed the convert-mailers-to-notifiers branch 2 times, most recently from 580fc1b to 53b5b25 Compare April 7, 2026 05:32
@pablobm
Copy link
Copy Markdown
Contributor Author

pablobm commented Apr 7, 2026

I had another look. There's a third way to do this which I now prefer. The following list supersedes the one above:

  1. (New) Don't link a record. Noticed seems to work well with this and feels the most natural to me.
    • record_type and record_id are nil/NULL.
    • This requires that we pass a recipient explicitly to #deliver_later.
  2. Have the record be the user instead of the trace.
    • I think this is a bit contrived.
  3. Use ephemeral notifications:
    • With these, the initial delivery must be synchronous (no deliver_later). Note that the individual notifications (emails, etc) are still async/queued.
    • This also requires passing the recipient explicitly (to #deliver in this case).
    • When it comes to on-site notifications, these would definitely require a separate model. That might be a good idea anyway; the proof of concept I put together last time (master...pablobm:notifications) had plenty room for improvement.
    • I have experimented with this option at master...pablobm:ephemeral-notifications

In common to all options, any additional arguments are stored in the params column, where we can put the trace data before deletion.

  • One potential issue is that the params column can get big as params[:error] may store an exception backtrace.
  • When rendering later (eg: on-site notifications), the data can be accessed at params[:trace_name] and params[:trace_description] to describe the notification.

Of the options above, this PR now implements option 1. I have also integrated this notification into my proof of concept at master...pablobm:notifications. This is currently deployed to my Dev Server setup, where it can be seen in action in the dashboard of user mapper (password password):

Proof of concept of a list of notifications, shown on the website. Two notifications are listed, one linking to a note that has received a comment, another describing a GPX import that has failed, including its name and description

@pablobm pablobm force-pushed the convert-mailers-to-notifiers branch 3 times, most recently from 67bd8b6 to 6ffac5e Compare April 15, 2026 09:48
pablobm added 2 commits April 20, 2026 13:55
The mailer method now needs to receive the recipient directly, instead
of extracting it from the trace record.
The previous code relied on `#deliver` (alias for `#deliver_now`) to
immediately deliver the email so that the trace could be deleted. To
work around this, we pass the details of the trace instead of the trace
record, which won't be available by the time the notifications are
ultimately delivered.

This means the trace is not there to be the `record`, so we are not
passing one. Since we can't get the recipient from the non-existing
record, we give the recipient explicitly to `#deliver_later`.

Additionally, the mailer method now needs to receive the recipient
directly, instead of extracting it from the trace record.
@pablobm pablobm force-pushed the convert-mailers-to-notifiers branch from 6ffac5e to d6e9739 Compare April 20, 2026 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants