Skip to content

Adding OKR checkin reminder email notifications

Darby Frey requested to merge okr-checkin-reminder-notifications into master

What does this MR do and why?

This MR is part of https://gitlab.com/gitlab-org/incubation-engineering/okr/meta/-/issues/19+, and enables the reminder notifications for which the setting was added in Adding checkin reminder setting quick action (!130371 - merged)

This MR adds a new Sidekiq cron job that runs once per day. The job uses Okrs::CheckinReminderKeyResultFinder to return an array of all opened work items of type key_result with assignees that have been configured with the notification cadence (weekly, twice-monthly, or monthly).

It then iterates over each item and sends individual emails to each assignee.

Screenshots or screen recordings

Email Screenshot

Screenshot_2023-09-12_at_3.39.36_PM

Database Review

The daily cron job introduces the three queries below via the finder class:

Finder Query

SELECT
  "issues".*
FROM
  "issues"
  INNER JOIN "issue_assignees" ON "issue_assignees"."issue_id" = "issues"."id"
  INNER JOIN "work_item_parent_links" ON "work_item_parent_links"."work_item_id" = "issues"."id"
  INNER JOIN "issues" "work_item_parents_issues" ON "work_item_parents_issues"."id" = "work_item_parent_links"."work_item_parent_id"
  LEFT OUTER JOIN "work_item_progresses" ON "work_item_progresses"."issue_id" = "issues"."id"
WHERE
  ("issues"."state_id" IN (1))
  AND (
    "issues"."work_item_type_id" = (
      SELECT
        "work_item_types"."id"
      FROM
        "work_item_types"
      WHERE
        "work_item_types"."base_type" = 6
      LIMIT
        1
    )
  )
  AND "work_item_parent_links"."work_item_parent_id" IN (
    WITH RECURSIVE "base_and_descendants" AS (
      (
        SELECT
          "issues"."id"
        FROM
          "issues"
          INNER JOIN "work_item_progresses" ON "work_item_progresses"."issue_id" = "issues"."id"
        WHERE
          "work_item_progresses"."reminder_frequency" = 1
          AND ("issues"."state_id" IN (1))
          AND (
            "issues"."work_item_type_id" = (
              SELECT
                "work_item_types"."id"
              FROM
                "work_item_types"
              WHERE
                "work_item_types"."base_type" = 5
              LIMIT
                1
            )
          )
          AND (
            NOT EXISTS (
              SELECT
              FROM
                work_item_parent_links
              WHERE
                work_item_id = issues.id
            )
          )
      )
      UNION
      (
        SELECT
          "issues"."id"
        FROM
          "issues",
          "base_and_descendants",
          "work_item_parent_links"
        WHERE
          "work_item_parent_links"."work_item_id" = "issues"."id"
          AND "work_item_parent_links"."work_item_parent_id" = "base_and_descendants"."id"
      )
    )
    SELECT
      "issues"."id"
    FROM
      "base_and_descendants" AS "issues"
  )
  AND (
    work_item_progresses.last_reminder_sent_at IS NULL
    OR work_item_progresses.last_reminder_sent_at <= '2023-09-13'
  )
GROUP BY
  "issues"."id"
ORDER BY
  "issues"."id" ASC
LIMIT
  1000

Query plan:

cold cache: https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/22655/commands/73037

warm cache: https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/22655/commands/73038

Update Query

The following update query is introduced in Okrs::CheckinReminderEmailsCronWorker#deliver_reminder.

UPDATE
  "work_item_progresses"
SET
  "updated_at" = '2023-09-13 18:22:46.209470',
  "last_reminder_sent_at" = '2023-09-13 18:22:46.208466'
WHERE
  "work_item_progresses"."issue_id" = 134631298

Query Plan: https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/22174/commands/71674

How to set up and validate locally

  1. Enable the okrs_mvc and okr_checkin_reminders feature flag locally. In the rails console, run:

    Feature.enable(:okrs_mvc)
    Feature.enable(:okr_checkin_reminders)
  2. Create some OKR seed data. In the rails console run:

    objective = WorkItem.create(
      title: "Sample Objective",
      author: User.first,
      project: Project.first,
      work_item_type_id: ::WorkItems::Type.default_by_type(:objective).id
    )
    
    objective.create_progress(reminder_frequency: 'weekly')
    
    key_result = WorkItem.create(
      title: "Sample Key Result",
      author: User.first,
      project: Project.first,
      work_item_type_id: ::WorkItems::Type.default_by_type(:key_result).id
    )
    
    WorkItems::ParentLink.create(
      work_item: key_result,
      work_item_parent: objective
    )
    
    key_result.assignees<<User.first
  3. Run the cron job. In the rails console, run:

    date = Date.today.next_week(:tuesday)
    Okrs::CheckinReminderEmailsCronWorker.new(date: date).perform
  4. Load Letter Opener in the browser and you should see the checkin notification email http://127.0.0.1:3000/rails/letter_opener/

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Darby Frey

Merge request reports