Seamless Async CPM Updates

Async CPM jobs are processed by Oracle's SPM queue, but the actual PHP code runs on any number of utility servers (likely based on some load-balancing logic for the pod). The custom CPM code itself runs in a looping PHP process that executes jobs from the queue, which means that the code stays in memory for a period of time. (To my knowledge, Oracle hasn't published any information as to exactly how long these processes run, however.) This causes two important results:

  1. Static properties on CPM classes stay in memory for multiple CPM executions
  2. When updated, the old code will continue to run for an unknown amount of time, before the PHP process is restarted with your new updates
I mentioned these in my Async CPM Development and "My Code's Not Updating!" post, but would like to go into a little more detail here.

1. Caching static data for performance

If you are retrieving config settings, doing CPHP lookups, or making API calls that pull back data that does not change often, you have the option of caching this data in static properties for a performance gain, since it will stay in memory for the life of the PHP process. Subsequent calls to your custom CPM code can then access the data from these static properties. For example, if you wanted to use a custom "dev mode" config setting in your CPM handler: class MyIncidentHandler implements RightNow\CPM\v1\ObjectEventHandler { public static $devMode = null; public static function apply($runMode, $action, $object, $cycles) { if (self::$devMode === null) { // If the dev mode config value hasn't been set, retrieve it from a custom config setting, then cache it in the static $devMode property self::$devMode = RightNow\Connect\v1_3\Configuration::fetch("CUSTOM_CFG_MYINCIDENTCPM_DEVMODE")->Value } if (self::$devMode) { // CPM logic to run only in dev mode (additional logging, etc) } // Normal CPM logic... } } Keep in mind that when doing this, there's no way to manually refresh the cache. So whenever you change your config setting, you need to wait for the current SPM processes to end (which can be a few seconds to many minutes).

UPDATE:

I've seen instances where an old SPM process will use previous cached data from up to 3 HOURS ago, so it is probably best to wait an entire day to allow for the cutover! The other option would be do force a direct DB query (without using custom configs or cached data), but this is obviously not ideal for performance.

2. Making async CPM updates without any downtime

The unfortunate result of Oracle's setup is that if you are trying to synchronize code updates to an async CPM with a DB change or some external dependency, it's more difficult because you don't have any control over when the code switch occurs. This means that if you want a seamless deployment without downtime, you need to ensure that the old version continues to work for a period of time alongside the new version. Your general deployment process might look something like this:

  1. Deploy a new version of any external functionality, code, DB changes, etc (ensuring that the old version still works)
  2. Deploy new async CPM code
  3. Wait for all SPM processes to finish (up to an hour to be safe, multiple utility servers can run the async CPM code, so waiting for a single one to switch over to the new code is not enough, you need to wait for them all!)
  4. Deprecate/remove any old external functionality, if necessary

For example, if you needed to change the name and properties of a database field referenced in your CPM:

  1. Deploy the new field
  2. Migrate existing data from the old field to the new (you now have two database fields with the same data)
  3. Deploy new async CPM code that references this new field
  4. Wait for SPM processes to finish (during this time, both old and new fields may be referenced!)
  5. Remove the old database field

Or if you are making a breaking change to an external API:

  1. Deploy a new version of your API alongside the old
  2. Deploy new async CPM code that calls the new API version
  3. Wait for SPM processes to finish (during this time, both old and new APIs may be called!)
  4. Optionally, remove the old version of your API, if needed
Zircon - This is a contributing Drupal Theme
Design by WeebPal.