[#215] Resolve "admins can undo recycle"
Closes #215 (closed)
Context
- since twilio numbers cost money ($1/mo/number) we periodically "recycle" them by deleting the channel info associated with a number and throwing the number back in the pile of inactive numbers able to be used for new channels
- we try to only recycle phone numbers for channels that people haven't used in a while. however, we might accidentally remove a channel that people still want to use. that would be suuuper sad!
- this MR implements behavior to give people a warning and a 24 hour "grace period" before recycling the phone nubmer for their channel
- it is particularly urgent to play now, since we want to recycle phone numbers for some fairly big channels before implementing channel sharding (so that we don't allocate entire containers to very large channels that nobody uses anymore!)
Behavior
WHEN a maintainer issues a recycle
command for a channel
- THEN the admins will receive a notification that says
Your channel is about to be deactivated due to lack of use. To continue using the channel, respond with any message in the next 24 hours.
- IF admins respond within 24 hours, THEN their channel will not be recycled
- IF admins do not respond withing 24 hours, THEN their channel will be recycled (and they will receive notification that says
Your channel is being deactivated due to lack of use. To create a new channel, visit https://signalboost.info
Changes
data model:
- add a
recycleRequest
table and model (that stores a phone number and when it was enqueued for recycling) - when a sysadmin uses the cli to POST to
phoneNumbers/recycle
, now, isntead of hittingphoneNumberRegistrar.recycle
(which would actually recycle the number), instead callrecycleRequestRepository.requestToRecycle
to create a newrecycleRequest
record
processing requests:
- add a job that runs every 24 hours and calls
phoneNumberRegistrar.processRecycleRequests
, which in turn delegates tophoneNumberRepository.evaluateRecycleRequests
-
evalutaeRecycleRequests
:- hits the DB to determine which phone numbers have been redeemed by their admins (by being used during the grace period) and which ones habe not been used and thus should be recycled.
- leaves as pending any requests for which the grace period has not yet expired, regardless of whether the channels have been used or not
- returns a
redeemed
/toRecycle
tuple back toprocessRecycleRequests
-
processRecycleRequests
takes this information and acts on it by:- recycling the unredeemed phone numbers (including notifying the channel members of the deactivation)
- deleting all the matured recycle requests
- notifying members of any redeemed phone numbers that their channels have not been deactivated
- notifying maintainers (who issued the original recycle request a day ago) of the results of their requests (ie: how many phone numbers were redeemed and recycled and if eany errors happened)
tech debt / side effects:
-
add add
util.repeateUntilCancelled
, use it to make all jobs cancellable (and cancel them in jobs.stop())- motivation: this prevents non-deterministic unti test failures by stopping functinos from being called over and over again in the test for the jobs module and thereby interfering with the unit tests for those functions elsewhere
-
util.repeateUntilCancelled
:- calls an async function repeatedly on an interval
- returns a function that, if invoked, cancels the next call of the async function
-
fix localization bug in
phoneNumberRegistrar.destroy
- was sending destruction notification in default lang (EN) to all recipients
- should send notification in reach recipient's lang
- to fix, provide new impl of
notifyMembersExcept
-
extract a
notifier
module with helpers for notifying members, admins, and channels -
refactor shared db destroy logic in recycle/destroy
- pull
destroy
logic into repository layer - leverage this to provide cleaner (less coupled) stubs in unit tests for the registrar layer
- add unit tests to ensure that destroy deletes associated models of channels
- pull
-
extract all recurring job scheduling logic from the repository layer to the jobs module
Edited by aguestuser