Restoring Deleted Data

Overview

When things go wrong and the wrong things get deleted, Faros has an easy mechanism to restore the deleted data. Our solution provides a way to tag deleted data with a session id and an associated API to undelete by session id. And even when a session id is not available, data can be restored with ad-hoc criteria.

Restoring Data Deleted By Sources

The easiest way to restore data is by session id and origin. For sources, we log the session id and origin prior to issuing reset mutations. To view the session id and origin, View Sync Logs for the desired Source and look for a log message like:

Resetting data before 2024-08-12T18:59:58.372Z for origin gv2-faros-jira-v2 with session id 703c91de-3a35-4117-ad45-a03f63048e29

Here we see the origin is gv2-faros-jira-v2 and session id is 703c91de-3a35-4117-ad45-a03f63048e29. Later in the log we see all the models that have been reset with this origin and session id.

Resetting tms_Project data for origin gv2-faros-jira-v2

Resetting tms_User data for origin gv2-faros-jira-v2

Resetting tms_Release data for origin gv2-faros-jira-v2

Resetting tms_TaskReleaseRelationship data for origin gv2-faros-jira-v2

Resetting tms_TaskBoard data for origin gv2-faros-jira-v2

Resetting tms_TaskBoardRelationship data for origin gv2-faros-jira-v2

Resetting tms_TaskBoardProjectRelationship data for origin gv2-faros-jira-v2

Resetting tms_ProjectReleaseRelationship data for origin gv2-faros-jira-v2

In the following step, we use the GraphiQL interface available under Query Ingested Data to restore the tms_TaskBoard data deleted in the reset above:

mutation restore {
  restoreDeleted(args: {
    model: "tms_TaskBoard", 
    session: "703c91de-3a35-4117-ad45-a03f63048e29", 
    origin: "gv2-faros-jira-v2"}) {
    rowCount
  }
}

If these restoration is successful, we will see the number of rows restored in the GraphiQL interface:

{
  "data": {
    "restoreDeleted": [
      {
        "rowCount": 1000
      }
    ]
  }
}

Restoring Other Data

Below we will cover two scenarios for recovering deleted data that was not deleted by a Source.

With Session Id and Origin

If you are going to be performing the delete via a GraphiQL mutation, you can set the session id within the same operation as the delete as follows:

mutation delete_w_session {
  ctx: setCtx(args: {session: "example"}) {
    success
  }
  del: delete_cicd_Build(where: {uid: {_eq: "u1"}}) {
    affected_rows
  }
}

Since you know the session id and origin you can use the restoreDeleted mutation discussed above as follows:

mutation restore_w_session {
  restoreDeleted(args: {
    model: "cicd_Build", 
    session: "example", 
    origin: "my-origin"}) {
    rowCount
  }
}

Without Session Id

If a session id was not used for the delete, we can still use an arbitrary predicate (i.e. an expression that evaluates to true for the data we want to restore). Suppose that we know the data we want to restore was deleted between 9 and 10am yesterday. Then we can restores the data as follows:

mutation restore {
  restoreDeletedPredicate(args: {
    model: "cicd_Build", 
    predicate: "_action_at BETWEEN '2024-08-11T09:00+07' AND '2024-08-11T10:00+07'"}) {
    rowCount
  }
}

Here we are using the special _action_at column that tracks when the data was deleted. In general, we can use any of the fields (e.g. refreshAt) of the deleted data expressed in snake case (e.g. refresh_at ).

A Final Note

Data can be deleted and restored repeatedly. Each time the data is deleted only the last copy of the data is retained. Therefore it is only possible to restore to the prior version of the data. Once data is recreated either by restore or recreation, restoring will have no effect. You will see this in the "rowCount": 0 result.