Set Contact Workflow

Setting the contact is more complicated than setting other properties like the push-token and requires an asynchronous workflow. In me-client service this is done using the client-changes topic and several subscription of it.

Monitoring

The current status of the workflow is visable in the me-client dashboard in google cloud monitoring.

Workflow

Workflow overview
  1. Client makes a request to the /v3/apps/:appCode/client/contact endpoint with the contact-field-id and the contact-field-value in the body.

  2. The endpoint controller publishes the payload to the client-change pubsub topic with following attributes

    • me-client:should_update_suite_contact_fields "true" or "false" depending on the fields available in the app.

    • me-client:is_anonymous "true" or "false" depending on the the i anonymous query param.

    • me-client:change_type set to "contact-field"

  3. A subscription of client-change filtered for only non-anonymous contact-field changes is consumed by the client-identified-contact-resolver worker. The worker resolves the contact-id from the contact-field.

  4. The contact-id change is published to the client-change topic.

  5. A subscription of client-contact filtered for only contact-id changes is consumed by the client-contact-id-writer worker which writes contact-id to the following places.

    • set contact property in contact-reference entry in dynamo

    • set contact data of the device in dynamo. This operation uses a separate timestamp to protect against old writes.

    • publish to client-state-contact-change topic

    • add device-log entry to the push-redis which can be listed in the UI.

Topics

client-change

Attributes

  • customer_id: the numeric id of the customer

  • client_id: the id used by the SDK to register the client

  • me-client:change_type: contact-field | contact-id | push-token | properties

  • me-client:should_update_suite_contact_fields: true | false (optional)

  • me-client:is_anonymous: true | false (optional)

Payload Example

{
  "eventTime": 123455667,
  "contactReference": "contact-ref",
  "change": {
    "contactField": {
      "contactFieldId": 123,
      "contactFieldValue": "contact-field-value"
    },
  }
  "clientState": {
    "appCode": "EMS-1234",
    "osVersion": "17.0"
  }
}

See models in GitHub for details.

Subscriptions

The work is split over multiple subscriptions with their corresponding workers, the split is done mostly due to differences in how the different parts of the work can be retried and scaled.

client-identified-contact-resolver

Subscribed to client-change with a filter attributes."me-client:change_type"="contact-field" AND attributes."me-client:is_anonymous"="false". Resolves the suite contact-id from the client-field. If the lookup fails it needs to be retried up to 48H since the contact might not have been imported into emarsys yet. The contact-id is published to the client-change topic with the change_type='contact-id' attribute set.

Since a pubsub message batch is either fully processed or redelivered the worker needs to register all resolved changes in some storage like redis to be able to do idempotency checks and filter out messages that have already been processed.

client-anonymous-contact-resolver

Subscribed to client-change with a filter attributes."me-client:change_type"="contact-field" AND attributes."me-client:is_anonymous"="true". Resolves the suite contact-id from the client-id if the client has already used anonymous contact, and if no existing contact is found it creates a new one. The contact-id is published to the client-change topic with the change_type='contact-id' attribute set.

It does not cause any problem if a message is processed twice so there is no need for an idempotency check in the worker.

client-contact-id-writer

Subscribed to client-change with filter attributes."me-client:change_type"="contact-id". The resolved contact-id needs to be written to several places. We must set contact property in contact-reference entry as well as the device entry in dynamo. Finally we also need to publish to client-state-contact-change topic to make sure it’s written to big-query and available for segmentation.

Since the event-time is used to avoid overwriting newer information there is no need for an idempotency check in the worker.

client-suite-contact-updater

Subscribed to client-change with filter attributes."me-client:should_update_suite_contact_fields"="true". Either the suite-contact updates are done directly in the worker or it publishes an AMQP message to the current worker. See worker implementation.