From f6b92e8c02487d62c12f371ab14375dd91a66810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Honor=C3=A9?= Date: Tue, 13 Feb 2024 13:20:00 +0000 Subject: [PATCH 1/2] documentation: the journey of a cohort item --- README.md | 11 +- docs/the-journey-of-a-cohort-item.md | 149 ++++++++++++++++++ .../1707822328.png | Bin 0 -> 27393 bytes 3 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 docs/the-journey-of-a-cohort-item.md create mode 100644 docs/the-journey-of-a-cohort-item/1707822328.png diff --git a/README.md b/README.md index b501f080..720b1e65 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,20 @@ The price migration engine is an orchestration engine used to perform controlled price migrations. It currently consists in a collection of lambdas designed to work together as a state machine. -For a general introduction to the principles of price migrations, see [price migrations from first principles](docs/price-migrations-from-first-principles.md) +### General Introduction to price migrations and the engine: + +- [An introduction to the geenral principles of price migrations](docs/price-migrations-from-first-principles.md) +- [The journey of a cohort item](docs/the-journey-of-a-cohort-item.md) +- [Notification periods](docs/notification-periods.md) + +### Operations: To set up a new cohort of subscriptions for price rise, see [cohort setup](docs/cohort-setup.md). -See readme for: +### Further documentation: * [State machine](stateMachine/README.md). * [Lambdas](lambda/README.md). * [Data stores](dynamoDb/README.md). -* [Notification periods](docs/notification-periods.md). For production troubleshooting, see the [troubleshooting document](docs/troubleshooting.md) diff --git a/docs/the-journey-of-a-cohort-item.md b/docs/the-journey-of-a-cohort-item.md new file mode 100644 index 00000000..3154499f --- /dev/null +++ b/docs/the-journey-of-a-cohort-item.md @@ -0,0 +1,149 @@ +## The journey of a cohort item + +In [Price migrations from first principles](./price-migrations-from-first-principles.md) we have seen what price migrations are and the general logic behind then. In this chapter let's have a closer look at the actual steps of a price migration following the journey of a cohort item in the engine. + +### What is a cohort item ? + +In the engine parlance a "cohort" is a set of subscription numbers that are part of a given price migration. Logically a "cohort item" would then be one of those numbers, but in fact it refers to a record in the Dynamo table where the engine maintains information about the price migration. + +One such cohort items seen in one of the Dynamo tables is shown below + +![](./the-journey-of-a-cohort-item/1707822328.png) + +The price migration may apply to a collection of subscriptions, but each subscription is price risen independently to the others, so to understand the logic of a migration we only need to follow the journey of one given subscription number. + +### Loading the subscription numbers and creating the Dynamo table. + +A price migration fundamentally need two ingredients + +1. The engine has been updated with the required code to encode the specificities of the migration (for instance specific features we implemented for Newspaper2024) + +2. Marketing has provided us with the list of subscription numbers of all the subscriptions that are part of the migration. That list comes as a file with one subscription number per line, like this + +``` +S-00000001 +S-00000002 +S-00000003 +S-00000004 +etc.. +``` + +At this point we need to have decided a name for the migration. Let's imagine we called it Newspaper2024. We locate the `price-migration-engine-prod` S3 bucket and create a directory called `Newspaper2024` and put in it two files. + +- salesforce-subscription-id-report.csv , which contains the list of subscription numbers +- excluded-subscription-ids.csv , an empty file + +(Note: we are soon going to change those conventions, because they are legacy names which have outstayed their welcome, this will come as a PR and this documentation will be updated, but at the time those lines are written, that's the convention.) + +The engine will not automagically pick up those files, it needs to be instructed to do so (this will not be covered here), but the effect of the Dynamo being created and the file having been loaded is that we now have a table with records and each record simply contains a subscription number. + +To help with understanding of the following steps let's use a (simplified) version of the CohortItem case class in the engine Scala Code + +``` +case class CohortItem( + subscriptionName : String, + processingStage : String, + currency : Option[String] = None, + billingPeriod : Option[String] = None, + oldPrice : Option[BigDecimal] = None, + estimatedNewPrice: Option[BigDecimal] = None, + startDate : Option[LocalDate] = None, + newPrice : Option[BigDecimal] = None, +) +``` + +So, when the subscription number is loaded into the table, the cohort item is essentially + +``` +CohortItem( + subscriptionName = "S-00000003" +) +``` + +With that said we need to indicate the processing stage an in that case it will be "ReadyForEstimation", so the cohort item is actually + +``` +CohortItem( + subscriptionName = "S-00000003" + processingStage = "ReadyForEstimation" +) +``` + +In this stage the engine essentially is saying "I have recorded this subscription number and it's ready to be Estimated" + +### The Estimation Stage + +The estimation stage has several purposes, in fact 3 main purposes + +1. Looking up metadata about the subscription in Zuora +2. Deciding what the post migration price is going to be +3. Deciding the start date, meaning the future billing date that the price rise is going apply. + +Considering our subscription number `S-00000003`, let's imagine that the subscription look up in Zuora reveal the following information: currency: `EUR`, billing period: `Monthly`, old price: `52`. The cohort item then becomes + +``` +CohortItem( + subscriptionName = "S-00000003" + processingStage = (...) + currency = Some("EUR") + billingPeriod = Some("Monthly") + oldPrice = Some(BigDecimal(52)) +) +``` + +Let's also assume that we know that the most migration price for this subscription (considering the type of product it carries) is `61`. In the case of the Newspaper2024 migration, we knew the post migration price of any of the products because it was actually hardcoded in the engine code (or computed using the `ChargeDistribution2024` algebra we introduced). The cohort items becomes + +``` +CohortItem( + subscriptionName = "S-00000003" + processingStage = (...) + currency = Some("EUR") + billingPeriod = Some("Monthly") + oldPrice = Some(BigDecimal(52)) + estimatedNewPrice = Some(BigDecimal(61)) +) +``` + +where "estimatedNewPrice" refers to the price post-rise. + +The last bit of data is the billing date that the price rise should be applied to. If you remember the explanations in Chapter 1, we saw that this date is computed considering a collection of factors (how old the subscription is, the billing period, how close the next natural billing period would be to the earliest possible time for notification and the need to wait at least 30 days etc). Let's imagine that the computed start date is `2024-05-10`. The cohort item becomes + +``` +CohortItem( + subscriptionName = "S-00000003" + processingStage = (...) + currency = Some("EUR") + billingPeriod = Some("Monthly") + oldPrice = Some(BigDecimal(52)) + estimatedNewPrice = Some(BigDecimal(61)) + startDate = Some(LocalDate.of(2024, 5, 10)) +) +``` + +At this stage the cohort items has been "estimated". Technically the cohort item is now in processing stage `EstimationComplete`. With that said, the engine is immediately going to create a record in Salesforce to mark the subscription in Salesforce with the new post migration price. At this point the processing stage is now `SalesforcePriceRiceCreationComplete`. + +``` +CohortItem( + subscriptionName = "S-00000003" + processingStage = "SalesforcePriceRiceCreationComplete" + currency = Some("EUR") + billingPeriod = Some("Monthly") + oldPrice = Some(BigDecimal(52)) + estimatedNewPrice = Some(BigDecimal(61)) + startDate = Some(LocalDate.of(2024, 5, 10)) +) +``` + +Then, the cohort item is going to.... sleep. It's going to sleep as long as it take for it to be at the start date minus about 40 days. This might take a few days or up to a year. + +### Notification and Amendment + +Today is now `LocalDate.of(2024, 5, 10)` minus about 40 days. The engine sees a subscription in `SalesforcePriceRiceCreationComplete` stage ready to be user notified. The engine is going to perform a certain number of look ups and will send a message to a queue that will eventually be delivered to Braze (triggering the sending of an email, or sending an additional request to an external company for a letter to be printed and delivered). + +The message to the user is going to mention the start date and the new estimated new price. The engine then notifies Salesforce that the user has been notified (for record keeping) and then will perform the amendment in Zuora, meaning will update Zuora with the fact that the subscription in Zuora is price risen and that the price rise is taking effect on the date that had already been decided during the estimation step. Note that this is the first and only moment that the engine performs and write operation in Zuora during the entire price rise process of that subscription. + +It is also important to notice that the amendment step only happened after the user notification step. This is to avoid a situation where there would be a bug in the engine or even just a long outage and causing subscriptions to be price risen in Zuora without the users having (yet) been notified. That would be illegal and put the Guardian in hot water. + +At this point the processing stage is `AmendmentComplete`. + +The last step is now another Salesforce update, where we inform Salesforce that the subscription in Zuora has been edited for price rise. Then the processing stage becomes `AmendmentWrittenToSalesforce`. This completes the price rise of subscription "S-00000003". diff --git a/docs/the-journey-of-a-cohort-item/1707822328.png b/docs/the-journey-of-a-cohort-item/1707822328.png new file mode 100644 index 0000000000000000000000000000000000000000..208b33547357a23cfd33c5d07541d6cce0613599 GIT binary patch literal 27393 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 McTIFEZacpCFZH?o%K!iX literal 0 HcmV?d00001 From dc6f9902f0a6c12adaba8b1baeb1353aa7c429f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Honor=C3=A9?= Date: Tue, 13 Feb 2024 13:34:56 +0000 Subject: [PATCH 2/2] add section on cancellations --- docs/the-journey-of-a-cohort-item.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/the-journey-of-a-cohort-item.md b/docs/the-journey-of-a-cohort-item.md index 3154499f..88f9a82d 100644 --- a/docs/the-journey-of-a-cohort-item.md +++ b/docs/the-journey-of-a-cohort-item.md @@ -147,3 +147,19 @@ It is also important to notice that the amendment step only happened after the u At this point the processing stage is `AmendmentComplete`. The last step is now another Salesforce update, where we inform Salesforce that the subscription in Zuora has been edited for price rise. Then the processing stage becomes `AmendmentWrittenToSalesforce`. This completes the price rise of subscription "S-00000003". + +### Cancellations + +As we have seen a cohort item / subscription can sleep for a long time before it is ready to move to notification process, but what happens if the subscription has been cancelled by the user in the meantime ? + +In such a case, the engine will detect that the subscription has been cancelled in Zuora and will move the cohort item to `Cancelled` processing stage + +``` +CohortItem( + subscriptionName = "S-00000003" + processingStage = "Cancelled" +) +``` + +Once the cohort item is in `Cancelled` state the engine will no longer touch it. Alike `AmendmentWrittenToSalesforce`, `Cancelled` is a final state for a cohort item. +