Implementation Reference
Config promotion
Promotion state
Promotion state is derived from the state of each table in the promotion group. Each table can be in one of the following states:
CREATED- the promotion was started, but celery workers have not started processing the table yet.EXPORT_FAILED- en error occurred while exporting the table.TARGET_INIT_FAILED- initialization of the target environment failed.PREVIEW_FAILED- an error occurred while generating the config promotion preview.MERGE_FAILED- an error occurred while merging the data into the target environment.ROLLBACK_FAILED- an error occurred while rolling back the target environment.EXPORT_RUNNING- the table is being exported into blob storage.TARGET_INIT_RUNNING- the target environment is being initialized.PREVIEW_RUNNING- the config promotion preview is being generated.MERGE_RUNNING- the data is being merged into the target environment.ROLLBACK_RUNNING- the target environment is being rolled back.EXPORT_COMPLETE- the table was successfully exported into blob storage, but merge has not started yet.TARGET_INIT_COMPLETE= the target environment was successfully initializedPREVIEW_COMPLETE- the config promotion preview was successfully generated.ROLLBACK_COMPLETE- the target environment was successfully rolled back.MERGE_COMPLETE- the data was successfully merged into the target environment.EXPORT_SKIPPED- export was skipped because the table is empty.PREVIEW_DISCARDED- users have discarded the config promotion preview.
If promotion tables have different states, the promotion state is determined by order in the list above, so if we have a promotion consisting of 3 tables,
one in MERGE_COMPLETE, one in MERGE_RUNNING and one in EXPORT_FAILED, the promotion state will be EXPORT_FAILED.
In a "happy path" scenario, we're looking into following state changes:
CREATED -> EXPORT_RUNNING -> EXPORT_COMPLETE -> TARGET_INIT_RUNNING -> TARGET_INIT_COMPLETE -> MERGE_RUNNING -> MERGE_COMPLETE.
Or when creating a config promotion preview:
CREATED -> EXPORT_RUNNING -> EXPORT_COMPLETE -> TARGET_INIT_RUNNING -> TARGET_INIT_COMPLETE -> PREVIEW_RUNNING -> PREVIEW_COMPLETE.
Public API
This section describes the endpoints that are used by the UI to create and manage Config promotion. Config promotion has several other endpoints that are used by the backend to manage the promotion state, perform internal operations but they are never called by the UI and should not be called directly by the users/QA.
Promotion list
GET /api/v1/promotions/ - returns a paginated list of promotions.
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "39376531-cfd7-4449-85a8-36bc296aea04", // unique id of the promotion
"createdBy": "user@hitachisolutions.com", // user who created the promotion
"duration": 1.2983508329780307, // total duration of the promotion operations on the worker
"exportDuration": 0.8854285289708059, // total duration of the export tasks for all tables
"uploadDuration": 0.13583119100076146, // total duration of the upload tasks for all tables
"targetInitDuration": 0.2770911130064633, // total duration of the target init tasks for all tables
"mergeDuration": 0, // total duration of the merge tasks for all tables
"status": "PREVIEW_FAILED", // current status of the promotion
"exportedRows": 4236, // total number of rows exported for all tables
"sourceEnvironment": "9519211a-9f8c-4dac-8e11-9210751f2ae8", // id of the source environment
"targetEnvironment": "57c9d42d-cac3-4bbb-ab9e-bb3a18cde833", // id of the target environment
"backup": true, // whether the target environment was backed up before the promotion
"createdAt": "2023-09-29T17:22:16.870734",
"updatedAt": "2023-09-29T17:22:16.870749"
}
]
}
Create promotion
POST /api/v1/promotions/ - create a new promotion.
{
"environmentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", // global id of the target environment
"commit": false, // whether the promotion should be committed or discarded
"filters": [ // optional filters
{
"groupId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", // global id of a promotion group
"values": [ // optional list of global ids of the top-level records that will be migrated
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
]
}
],
"backup": true // whether the target environment should be backed up before the promotion
}
Promote sources (DatabaseList)
POST /api/v1/promotions/sources/ - create a promotion for a DatabaseList:
Request:
{
"environmentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", // global id of the target environment
"globalIds": [ // optional list of global ids of the top-level records that will be migrated
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
]
}
Promotion details
GET /api/v1/promotions/{id}/ - same as promotion list, but returns a single non-paginated promotion.
Commit config promotion preview
POST /api/v1/promotions/{id}/commit/ - if promotion was created with commit set to false, this endpoint can be used to commit the promotion. This will change the state of the promotion to MERGE_COMPLETE if successful. Can only be executed if promotion is in PREVIEW_COMPLETE state.
Example request:
{
"force": true // force the commit even if target environment data was modified since the preview was generated, by default this is set to false.
}
Discard config promotion preview
PATCH /api/v1/promotions/{id}/discard/ - discard the promotion preview. This will change the state of the promotion to PREVIEW_DISCARDED. Can only be executed if promotion is in PREVIEW_COMPLETE state.
ZIP Downloads
GET /api/v1/promotions/{id}/downloads/ - download links for export and preview ZIP files. May return null values if zip files are not created yet.
Rollback promotion
POST /api/v1/promotions/{id}/rollback/ - rollback a target database to a state before the promotion was merged. Can only be executed if promotion is in MERGE_COMPLETE state. The records that were added to target database after the promotion was merged will be deleted.
Promotion exports
GET /api/v1/promotions/{promotionId}/exports/ - provides download urls for export files for individual tables in the promotion. May return null values if export files are not created yet.
Promotion entities
GET /api/v1/promotions/entities/ - a paginated list of all promotable entities, each item has the following schema:
{
"id": "2690f7fe-f4f3-47d0-b838-b31db0a6f9d6", // global id of the entity
"tableName": "Entity", // table name
"schemaName": "model", // schema name
"promotionGroup": "e96979e2-d7b4-4d92-864b-3fd528180ecd", // global id of the promotion group entity belongs to
"parents": [ // list of global ids of the parent entities
"df9b07c3-04b0-477a-8b44-4a9b06688e19",
"8a90bde7-1260-41e7-896f-dd87cafce27d"
]
}
Environment list
GET /api/v1/promotions/environments/ - a list of available promotion environments, returns a non-paginated list:
[
{
"name": "Development",
"globalId": "9519211a-9f8c-4dac-8e11-9210751f2ae8",
"stageOrder": 0
},
{
"name": "Continuous Deployment",
"globalId": "57c9d42d-cac3-4bbb-ab9e-bb3a18cde833",
"stageOrder": 1
}
]
Promotion groups
GET /api/v1/promotions/groups/ - a list of promotion groups.
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": "e96979e2-d7b4-4d92-864b-3fd528180ecd",
"name": "Model Group",
"filterTable": null
},
{
"id": "61eba498-ac2b-4d73-ba26-5d9321553960",
"name": "Source Group",
"filterTable": null
}
]
}
Implementation details
High level overview of the Config promotion process
sequenceDiagram
box rgb(50, 92, 168) Source Environment
participant Source API
participant Source DB
participant Source Worker
end
participant Blob Storage
box rgb(40, 158, 107) Target Environment
participant Target API
participant Target DB
participant Target Worker
end
Source API ->> Source DB: Crate promotion record
Source API ->> Source Worker: Create export task for each table
Source Worker ->> Source DB: Generate export CSV
Source Worker ->> Blob Storage: Upload exports CSVs
Source Worker ->> Source DB: Update promotion state
Source Worker ->> Blob Storage: Upload exports zip
Source Worker ->> Target API: Trigger merge
Target API ->> Target Worker: Run target initialization
Target Worker ->> Target DB: Create a backup
Target Worker ->> Blob Storage: Upload backup
Target Worker ->> Target DB: Start merge
Target DB ->> Blob Storage: Retrieve source exports
Target DB ->> Target DB: Merge source exports into target
Target Worker ->> Source API: Update promotion state
Source API ->> Source DB: Update promotion
Target Worker ->> Blob Storage: Upload preview csv zip
The promotion process explained
-
User retrieves a global id of a target environment using
GET /api/v1/promotions/environments/endpoint. This endpoint is powered by frondoor service discovery capabilities. Target environment can be any environment except the current environment. -
(Optional) Build the promotion filters for the promotion:
- Retrieve a promotion group list using
GET /api/v1/promotions/groups/endpoint. - Specify a list of global ids of the top-level records that will be migrated. This is optional, if no promotion filters are specified, all the data from the Source environment will be migrated.
- Retrieve a promotion group list using
-
Make a
POST /api/v1/promotions/request to create a new promotion. This will create a new promotion record in the source environment and trigger the export process. -
Before creating a promotion record we check if there are any other unfinished promotions for the same target environment. If there are any, we return an error. This is done to prevent multiple promotions from running at the same time.
-
We create a promotion record in source environment database with state
CREATED. This promotion record contains a user, source and target environment ids, whether backup should be created. -
If promotion filter values were provided - we also create a promotion filters record for each promotion filter values. This record contains a promotion id, promotion group id and a list of global ids of the top-level records that will be migrated.
-
We query promotable entities table to get a list of all promotion tables for given promotion groups and sort the list by the dependency tree (i.e. if table A depends on table B, table B will be first in the list).
-
API spawns a group of
export_entitytasks on a Source environment celery worker for each entity:- It sets the state of promotion table to
EXPORT_RUNNING. PrepareDataForExportstored procedure is executed to generate a dump of the table data and include row hashes.- If the table is empty, we set the state of the table to
EXPORT_SKIPPEDand skip the rest of the steps. - The export csv file is uploaded to blob storage into
"{promotion_id}/export"directory. - Worker updates the state of promotion table to
EXPORT_COMPLETEand saves the various metadata (export durations, blob path). - If exists, we pass the blob path of a previous promotion to the
trigger_mergetask (later used during merge). - It passes export results to the
trigger_mergeandzip_blob_directorytasks.
- It sets the state of promotion table to
9.zip_blob_directory is called for the "{promotion_id}/export" directory which produces a single zip file with contents of the directory.
- Trigger merge makes a request to
PUT /promotions/mergeendpoint on a target environment with the results of export in a body
{
"promotionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", // global id of the promotion
"sourceEnvironmentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", // global id of the source environment
"commit": false, // passed from the original request
"backup": true, // passed from the original request
"exports": [
{
"entityName": "field_list", // promotable entity name
"tableId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", // global id of the promotion table
"blobName": "foo/field_list.csv",
"prevBlobName": "bar/field_list.csv",
}
],
"filters": {
// filters are passed from the original request
}
}
-
Merge on target environment spawns a group of
init_targettasks for each export from the request:- If backup was not requested and
prevBlobNameis present, this task is skipped. - Celery worker on a target environment makes a call to
PATCH /api/v1/promotions/{promotionId}/tables/{id}/on source environment to update the state of the table toTARGET_INIT_RUNNING. - If previous entity is not found for export, we execute
PrepareDataForExport(this is the same procedure that is used for exporton source environment) to generate "init" dump of table on a target environment. This is treated as a previous promotion during merge. Init file is uploaded to"{promotion_id}/init"directory. - If backup was requested the contents of the table are backed up into
"{promotion_id}/backup"directory as csv. PATCH /api/v1/promotions/{promotionId}/tables/{id}/is called to update the state of the table toTARGET_INIT_COMPLETE.
- If backup was not requested and
-
mergetask is called with the list of export:- For each promotable entity we make a request to source environment to update the state of the table to
MERGE_RUNNING. MergePromotableDatais executed with a path to the export file in blob storage and a path to the previous export file in blob storage or a path to the init file if previous export file is not found:- The procedure inserts data from the export file into a temporary table.
- It updates the primary keys of the temporary table to match the primary keys of the target table.
- The data from previous or init file is inserted into another temporary table.
MergePromotableDatais executed which dynamically generates aMERGEstatement and outputs a data for config promotion preview .- Config promotion preview data is uploaded to
"{promotion_id}/preview"directory. - If
commitistrue, the transaction is committed and state of the promotion on Source environment is updated toMERGE_COMPLETE. - If
commitisfalse, the transaction is rolled back and state of the promotion on Source environment is updated toPREVIEW_COMPLETE.
- For each promotable entity we make a request to source environment to update the state of the table to
-
zip_blob_directoryis called for the"{promotion_id}/preview"directory which produces a single zip file with contents of the directory.