OCS Audience Service và luồng Segment nguồn OCS

1. Mục tiêu

Tài liệu này mô tả phương án lấy tệp khách hàng từ OCS/Hadoop để sử dụng trong Segment và Campaign của LaoAds V2.

Thiết kế cần đáp ứng các yêu cầu sau:

2. Phạm vi thay đổi

2.1. Phần giữ nguyên

Các thành phần sau tiếp tục được sử dụng:

2.2. Phần loại bỏ khỏi luồng Segment QUERY

user_lao_id_field_config, UserFieldController và các lớp LaoID liên quan chỉ được xóa sau khi rà soát chắc chắn không còn module khác sử dụng. Nếu vẫn có luồng cũ phụ thuộc, đánh dấu TODO và tách việc loại bỏ sang task riêng.

Luồng OCS kế thừa cách dựng form điều kiện của user_lao_id_field_config, nhưng không dùng chung collection. OCS có nhiều bảng và cần thêm metadata về quyền select, quyền filter, operator cùng cách ánh xạ kết quả nên phải có model riêng.

2.3. Các thành phần mới

ocs_dataset_config
    Danh mục bảng OCS mà LaoAds cho phép hiển thị trên FE.

ocs_dataset_field_config
    Danh mục cột của từng bảng, phát triển từ pattern user_lao_id_field_config.

queryDefinition trong segment
    Định nghĩa bảng, cột và điều kiện lọc mà người dùng đã chọn.

segment_build_job
    Theo dõi tiến độ build SegmentVersion, retry và quyền xử lý giữa nhiều worker.

OCS Audience Service
    Kiểm tra query definition, đọc Hadoop và stream kết quả về Segment Import Worker.

3. Kiến trúc tổng thể

[Diagram]

3.1. Trách nhiệm của từng hệ thống

Hệ thống Trách nhiệm
Frontend Hiển thị danh sách bảng/cột do LaoAds cung cấp, tạo điều kiện lọc và gửi cấu hình Segment.
LaoAds API Quản lý danh mục OCS, validate request, gọi OCS verify, lưu Segment và cung cấp API cho FE.
MongoDB Lưu metadata, trạng thái SegmentVersion, CampaignRun và build job; không lưu hàng triệu member.
Segment Import Worker Gọi stream OCS, normalize dữ liệu, insert ClickHouse theo batch và quản lý retry.
OCS Audience Service Kiểm tra bảng/cột thực tế, tạo câu SQL an toàn, bind parameter và stream ResultSet.
Hadoop Nguồn dữ liệu OCS, chỉ cho tài khoản service đọc.
ClickHouse Lưu member dung lượng lớn và phục vụ xem, tạo target, thống kê.

4. Luồng giao diện

[Diagram]

4.1. API FE sử dụng để tạo Segment

GET  /ocs-datasets
GET  /ocs-datasets/{datasetCode}/columns
POST /segments
PUT  /segments/{segmentId}

Hai API /ocs-datasets đọc dữ liệu từ MongoDB của LaoAds, không gọi sang OCS theo từng request của FE.

Cách dựng giao diện giữ cùng nguyên tắc với luồng LaoID cũ:

  1. user_lao_id_field_config cũ trả metadata field để FE dựng điều kiện.
  2. Luồng OCS trả ocs_dataset_field_config của bảng đã chọn.
  3. FE dựa vào dataType để chọn loại input và dựa vào allowedOperators để hiển thị toán tử.
  4. FE gửi fieldCode; backend mới resolve sang columnName vật lý.

Khác biệt là LaoID cũ chỉ có một bảng user_basic_info, còn OCS có nhiều bảng. Vì vậy FE phải chọn datasetCode trước rồi mới tải field của dataset đó.

Response field cho FE tham khảo:

[
  {
    "fieldCode": "DATA_USAGE_MB",
    "label": "Dung lượng Data đã sử dụng",
    "dataType": "NUMBER",
    "selectable": true,
    "filterable": true,
    "targetField": "ATTRIBUTE",
    "allowedOperators": [
      "EQUALS",
      "GREATER_THAN",
      "GREATER_THAN_OR_EQUALS",
      "LESS_THAN",
      "BETWEEN"
    ]
  }
]

Backend chọn label theo X-Accept-Language giống API /user-field hiện tại; FE không phải tự chọn giữa labelVi, labelLo, labelEnlabelZh.

4.2. Các trường FE cần gửi

Request tham khảo:

{
  "name": "Thuê bao có nhu cầu nâng cấp gói Data",
  "description": "Tệp chạy chiến dịch nâng cấp Data",
  "type": "QUERY",
  "segmentType": "DYNAMIC",
  "queryDefinition": {
    "datasetCode": "SUBSCRIBER_DAILY",
    "selectedFields": [
      "MSISDN",
      "FIRST_NAME",
      "PACKAGE_CODE"
    ],
    "conditions": [
      {
        "fieldCode": "SNAPSHOT_DATE",
        "operator": "EQUALS",
        "valueSource": "RUN_DATE",
        "dayOffset": -1
      },
      {
        "fieldCode": "DATA_USAGE_MB",
        "operator": "GREATER_THAN_OR_EQUALS",
        "valueSource": "FIXED",
        "value": 5120,
        "relation": "AND"
      }
    ]
  }
}

Quy tắc:

4.3. Màn chi tiết Segment

Màn chi tiết hiển thị:

Khi Segment mới được tạo nhưng chưa chạy Campaign, danh sách version có thể rỗng. Đây là trạng thái hợp lệ vì Segment QUERY chỉ lưu định nghĩa; dữ liệu được build khi Campaign chạy.

5. Danh mục bảng và cột OCS trong MongoDB

5.1. Collection ocs_dataset_config

Mỗi document đại diện cho một bảng OCS mà LaoAds cho phép người dùng chọn.

{
  "_id": "subscriber-daily",
  "datasetCode": "SUBSCRIBER_DAILY",
  "displayName": "Thông tin thuê bao theo ngày",
  "description": "Dữ liệu thuê bao được chốt theo ngày",
  "schemaName": "ocs",
  "tableName": "subscriber_daily",
  "status": "ACTIVE",
  "requiredFilterFieldCodes": [
    "SNAPSHOT_DATE"
  ],
  "maxQueryRangeDays": 31,
  "createdAt": "2026-07-29T00:00:00Z",
  "updatedAt": "2026-07-29T00:00:00Z"
}

Các field thuộc bảng không nhúng vào document này. Chúng được lưu riêng trong ocs_dataset_field_config để giữ cách quản lý gần giống user_lao_id_field_config và tránh document bảng tăng kích thước khi số cột nhiều.

5.2. Collection ocs_dataset_field_config

Mỗi document đại diện cho một cột được phép hiển thị hoặc dùng làm điều kiện của một dataset.

{
  "_id": "subscriber-daily-msisdn",
  "datasetCode": "SUBSCRIBER_DAILY",
  "fieldCode": "MSISDN",
  "columnName": "isdn",
  "label": "Phone number",
  "labelEn": "Phone number",
  "labelVi": "Số thuê bao",
  "labelLo": "ເບີໂທ",
  "labelZh": "电话号码",
  "dataType": "STRING",
  "selectable": true,
  "filterable": true,
  "targetField": "PHONE",
  "allowedOperators": [
    "EQUALS",
    "IN"
  ],
  "status": "ACTIVE",
  "sortOrder": 1,
  "createdAt": "2026-07-29T00:00:00Z",
  "updatedAt": "2026-07-29T00:00:00Z"
}

Ví dụ field chỉ dùng để lọc theo ngày:

{
  "datasetCode": "SUBSCRIBER_DAILY",
  "fieldCode": "SNAPSHOT_DATE",
  "columnName": "snapshot_date",
  "labelEn": "Snapshot date",
  "labelVi": "Ngày dữ liệu",
  "labelLo": "ວັນທີຂໍ້ມູນ",
  "labelZh": "数据日期",
  "dataType": "DATE",
  "selectable": false,
  "filterable": true,
  "targetField": "ATTRIBUTE",
  "allowedOperators": [
    "EQUALS",
    "BETWEEN"
  ],
  "status": "ACTIVE",
  "sortOrder": 2
}

5.3. So sánh với user_lao_id_field_config

user_lao_id_field_config ocs_dataset_field_config mới Ghi chú
key fieldCode Mã field FE gửi về.
table Quan hệ qua datasetCode OCS phải chọn dataset trước vì có nhiều bảng.
fieldName hoặc key columnName Tên cột vật lý, không trả cho FE nếu không cần.
label, labelEn, labelVi, labelLo, labelZh Giữ nguyên Tiếp tục hỗ trợ nhãn theo ngôn ngữ hiện tại.
dataType Giữ nguyên nhưng chuyển thành enum FE dùng để dựng đúng input.
Chưa có selectable Xác định cột có được lấy ra làm dữ liệu người nhận không.
Chưa có filterable Xác định cột có được viết điều kiện không.
Chưa có allowedOperators Không cho FE tự gửi operator bất kỳ.
Chưa có targetField Ánh xạ về phone, email, tên hoặc attribute.
Chưa có status, sortOrder Ẩn field và sắp xếp giao diện mà không xóa dữ liệu.

Không tái sử dụng trực tiếp UserLaoIdFieldConfigModel vì model đó có key unique toàn hệ thống. Trong OCS, hai bảng khác nhau có thể cùng có field MSISDN, nên uniqueness phải nằm trong phạm vi datasetCode.

5.4. Ý nghĩa các trường

Trường Ý nghĩa
datasetCode Mã ổn định FE và Segment sử dụng; không phụ thuộc tên bảng hiển thị.
schemaName, tableName Bảng vật lý trong Hadoop. Chỉ backend sử dụng.
status ACTIVE cho phép tạo/sửa Segment; INACTIVE ẩn khỏi lựa chọn mới.
requiredFilterFieldCodes Các điều kiện bắt buộc để tránh query quét toàn bộ Hadoop.
maxQueryRangeDays Khoảng ngày tối đa được phép query nếu dataset có filter theo thời gian.
fieldCode Mã field ổn định FE gửi về.
columnName Tên cột vật lý trong Hadoop.
dataType STRING, NUMBER, DATE, DATETIME, BOOLEAN.
selectable Field có được đưa vào kết quả hay không.
filterable Field có được dùng làm điều kiện lọc hay không.
targetField Ánh xạ kết quả về PHONE, EMAIL, FIRST_NAME, LAST_NAME hoặc ATTRIBUTE.
allowedOperators Danh sách toán tử FE được phép hiển thị và backend chấp nhận.
labelEn, labelVi, labelLo, labelZh Nhãn field theo ngôn ngữ hiện tại của người dùng.
sortOrder Thứ tự field trên giao diện.

5.5. Index MongoDB

ocs_dataset_config:
  (datasetCode) unique
  (schemaName, tableName) unique
  (status, displayName)

ocs_dataset_field_config:
  (datasetCode, fieldCode) unique
  (datasetCode, columnName) unique
  (datasetCode, status, sortOrder)

API lấy field luôn có datasetCode; các index bắt đầu bằng datasetCode để query đúng phạm vi bảng và không quét toàn collection.

5.6. Cách cập nhật khi OCS thay đổi schema

[Diagram]

Hai API catalog của OCS chỉ dành cho kỹ sư kiểm tra bằng Swagger, curl hoặc Postman. LaoAds không gọi hai API này trong luồng tạo Segment hoặc chạy Campaign.

6. Dữ liệu lưu trong segment

6.1. Bổ sung queryDefinition

{
  "type": "QUERY",
  "segmentType": "DYNAMIC",
  "refreshMode": "PER_RUN",
  "queryDefinition": {
    "datasetCode": "SUBSCRIBER_DAILY",
    "schemaName": "ocs",
    "tableName": "subscriber_daily",
    "selectedColumns": [
      {
        "fieldCode": "MSISDN",
        "columnName": "isdn",
        "dataType": "STRING",
        "targetField": "PHONE"
      }
    ],
    "conditions": [],
    "definitionChecksum": "sha256...",
    "verifiedAt": "2026-07-29T10:00:00Z"
  }
}

LaoAds lưu cả mã logic và tên vật lý đã được resolve tại thời điểm lưu Segment. Vì vậy việc sửa ocs_dataset_config hoặc ocs_dataset_field_config không âm thầm làm thay đổi một Segment đang hoạt động.

Nếu schema OCS thay đổi:

  1. Cập nhật ocs_dataset_configocs_dataset_field_config.
  2. Mở và lưu lại Segment liên quan.
  3. Backend resolve lại column, gọi OCS verify và tạo definitionChecksum mới.
  4. Các CampaignRun đã tạo trước đó vẫn giữ source snapshot cũ; Run mới dùng định nghĩa mới.

6.2. SegmentSourceSnapshotModel

Khi build một version, phải snapshot đầy đủ:

type
queryDefinition
resolvedConditions
sourceDataAsOf
definitionChecksum

resolvedConditions là điều kiện sau khi các giá trị như RUN_DATE đã được chuyển thành giá trị cụ thể. Snapshot này không được sửa sau khi SegmentVersion được tạo.

6.3. segment_version

Giữ collection hiện tại và bổ sung thông tin nguồn cần thiết:

companyId
segmentId
versionNo
sourceType = QUERY
sourceDefinitionSnapshot
status = BUILDING | READY | FAILED | ARCHIVED
totalMemberCount
validMemberCount
invalidMemberCount
builtAt
completedAt
errorSummary

Version READY là bất biến. Sửa Segment không cập nhật member hoặc source snapshot của version cũ.

7. Hợp đồng OCS Audience Service

7.1. API catalog dùng để kiểm tra thủ công

GET /internal/v1/catalog/tables
GET /internal/v1/catalog/tables/{schemaName}/{tableName}/columns

API chỉ trả các bảng/cột mà JDBC service account nhìn thấy. API phải được bảo vệ bằng mạng nội bộ và xác thực dành cho kỹ sư vận hành.

7.2. API verify được LaoAds gọi

POST /internal/v1/audiences/verify

Request tham khảo:

{
  "requestId": "verify-uuid",
  "schemaName": "ocs",
  "tableName": "subscriber_daily",
  "selectedColumns": [
    {
      "columnName": "isdn",
      "dataType": "STRING",
      "targetField": "PHONE"
    }
  ],
  "conditions": [
    {
      "columnName": "snapshot_date",
      "dataType": "DATE",
      "operator": "EQUALS",
      "value": "2026-07-28"
    }
  ]
}

OCS Service kiểm tra:

Response tham khảo:

{
  "success": true,
  "data": {
    "valid": true,
    "queryChecksum": "sha256...",
    "warnings": []
  }
}

7.3. API stream được Worker gọi

POST /internal/v1/audiences/stream
Content-Type: application/json
Accept: application/x-ndjson

Request chứa:

requestId
companyId
segmentId
segmentVersionId
importAttemptId
schemaName
tableName
selectedColumns
resolvedConditions
queryChecksum

OCS Service không tin queryChecksum thay cho validation. Service vẫn kiểm tra request và compile câu query từ cấu trúc đã nhận trước khi thực thi.

7.4. Định dạng NDJSON

Mỗi dòng là một JSON độc lập:

{"recordType":"START","requestId":"...","startedAt":"..."}
{"recordType":"DATA","phone":"85620...","email":null,"firstName":"Somchai","lastName":null,"attributes":{"packageCode":"PKG01"}}
{"recordType":"DATA","phone":"85620...","email":"user@example.com","firstName":null,"lastName":null,"attributes":{}}
{"recordType":"END","requestId":"...","rowCount":250000,"completedAt":"..."}

Nếu lỗi xảy ra trước khi response bắt đầu, OCS trả HTTP status và error body chuẩn. Nếu lỗi xảy ra sau khi stream đã bắt đầu, OCS ghi một dòng ERROR cuối cùng rồi đóng stream:

{"recordType":"ERROR","errorCode":"OCS_STREAM0004","message":"Hadoop query interrupted"}

Worker chỉ chuyển version sang READY khi nhận đúng END và đối soát được số dòng. Mất kết nối hoặc nhận ERROR luôn được coi là attempt thất bại.

7.5. Tại sao stream không làm tràn RAM

OCS Service phải:

Segment Import Worker phải:

8. Luồng tạo và sửa Segment

[Diagram]

Khi sửa Segment:

9. Luồng CampaignRun và build SegmentVersion

[Diagram]

9.1. buildKey và trường hợp nhiều Campaign dùng chung Segment

buildKey được tạo từ:

companyId
segmentId
definitionChecksum
resolvedConditions
sourceDataAsOf

Sau đó canonicalize dữ liệu và băm SHA-256.

Nếu nhiều CampaignRun cùng sử dụng đúng một Segment, cùng điều kiện đã resolve và cùng thời điểm dữ liệu:

Nếu ngày dữ liệu, điều kiện hoặc định nghĩa Segment khác nhau thì buildKey khác và phải build version mới.

9.2. Collection segment_build_job

id
companyId
segmentId
segmentVersionId
buildKey
status: QUEUED | RUNNING | RETRY_WAIT | COMPLETED | FAILED
attemptNo
currentAttemptId
leaseOwner
leaseUntil
lastHeartbeatAt
processedCount
validCount
invalidCount
nextRetryAt
errorCode
errorSummary
createdAt
updatedAt

Index bắt buộc:

(buildKey) unique
(status, nextRetryAt)
(status, leaseUntil)
(segmentId, createdAt desc)

leaseOwnerleaseUntil đảm bảo nhiều worker không xử lý đồng thời cùng một job. Worker phải gia hạn lease trong khi stream; node khác chỉ được claim khi lease hết hạn.

9.3. Bổ sung ClickHouse segment_version_member

Giữ schema hiện tại và bổ sung:

import_attempt_id String
source_data_as_of Nullable(DateTime64(3))

import_attempt_id giúp phân biệt dữ liệu của từng lần thử. Khi retry, dữ liệu của attempt thất bại không được tính vào version hoàn chỉnh.

Phương án đọc an toàn:

10. Xử lý lỗi và retry

[Diagram]

Quy tắc:

11. Phân chia task

Các task dưới đây đủ lớn để giao độc lập nhưng vẫn có hợp đồng rõ giữa các nhóm. Không tách riêng task kiểm thử; mỗi task phải có unit test, integration test và log trong phạm vi của task đó.

Task 1 - Xây dựng OCS Audience Service

Mục tiêu

Tạo JAR độc lập triển khai trong OCS, có khả năng kiểm tra metadata, verify cấu hình audience và stream dữ liệu Hadoop mà không giữ toàn bộ kết quả trong RAM.

Phạm vi

API bàn giao

GET  /internal/v1/catalog/tables
GET  /internal/v1/catalog/tables/{schemaName}/{tableName}/columns
POST /internal/v1/audiences/verify
POST /internal/v1/audiences/stream
GET  /actuator/health
GET  /actuator/prometheus

Các trường hợp phải xử lý

Trường hợp Xử lý
Table hoặc column không tồn tại Trả error code, không mở stream.
Kiểu value không khớp column Trả field error tương ứng.
Operator không hỗ trợ Từ chối request.
Query timeout Cancel statement, đóng stream, ghi log thời gian và requestId.
Client ngắt kết nối Dừng đọc ResultSet và giải phóng connection.
Hadoop trả hàng triệu dòng Stream liên tục, bộ nhớ không tăng theo tổng số dòng.
JDBC pool hết connection Fail fast với mã lỗi quá tải, không chờ vô hạn.

Kết quả bàn giao

Task 2 - LaoAds BE quản lý danh mục bảng và cột OCS

Mục tiêu

Tạo nguồn cấu hình duy nhất trong LaoAds để FE chỉ nhìn thấy các bảng, cột và operator được phép sử dụng.

Phạm vi

MongoDB thay đổi

New: ocs_dataset_config
New: ocs_dataset_field_config

API bàn giao

GET  /ocs-datasets
GET  /ocs-datasets/{datasetCode}/columns
POST /admin/ocs-datasets/search
POST /admin/ocs-datasets
PUT  /admin/ocs-datasets/{datasetCode}
POST /admin/ocs-datasets/{datasetCode}/activate
POST /admin/ocs-datasets/{datasetCode}/inactivate
POST /admin/ocs-datasets/{datasetCode}/fields
PUT  /admin/ocs-datasets/{datasetCode}/fields/{fieldCode}
POST /admin/ocs-datasets/{datasetCode}/fields/{fieldCode}/activate
POST /admin/ocs-datasets/{datasetCode}/fields/{fieldCode}/inactivate

Luồng cập nhật schema

  1. Kỹ sư gọi hai API catalog của OCS để kiểm tra thủ công.
  2. Kỹ sư cập nhật cấu hình bằng migration hoặc API admin LaoAds.
  3. LaoAds validate và lưu Mongo.
  4. Cache danh mục được xóa.
  5. FE tải lại sẽ thấy dữ liệu mới.

Kết quả bàn giao

Task 3 - LaoAds BE chuyển Segment QUERY từ LaoID sang OCS

Mục tiêu

Thay luồng QUERY hiện tại bằng định nghĩa OCS có cấu trúc, verify trước khi lưu và giữ nguyên kiến trúc Segment/SegmentVersion.

Phạm vi

Code hiện tại bị ảnh hưởng

SegmentModel
SegmentSourceSnapshotModel
CreateSegmentRequest / Update Segment request
SegmentDto
SegmentService
CampaignService validation
CustomerQueryBuilder
UserFieldController và user_lao_id_field_config sau khi kiểm tra dependency

Các trường hợp phải xử lý

Trường hợp Xử lý
Dataset INACTIVE Không cho tạo/sửa Segment mới.
Field không thuộc dataset Trả lỗi đúng field.
Field không selectable nhưng nằm trong selectedFields Từ chối.
Field không filterable nhưng được dùng làm condition Từ chối.
Thiếu required filter Từ chối để tránh full scan.
Khoảng ngày vượt giới hạn Từ chối và trả giới hạn cho FE.
Không có PHONE/EMAIL Từ chối vì không tạo được recipient.
OCS verify timeout Không lưu cấu hình mới; Segment cũ không bị thay đổi.
Sửa Segment sau khi đã có Run Run cũ giữ snapshot; Run mới dùng định nghĩa mới.
Segment QUERY test cũ Chỉnh sửa và verify lại hoặc xóa rồi tạo mới; không tự diễn giải điều kiện LaoID thành OCS.

Kết quả bàn giao

Task 4 - FE tạo và xem Segment nguồn OCS

Mục tiêu

Cho người dùng tạo Segment OCS bằng các điều khiển rõ ràng mà không cần biết SQL hoặc tên cột vật lý.

Phạm vi

Các trường hợp phải xử lý

Trường hợp Xử lý FE
Dataset bị inactivate khi form đang mở Submit lỗi thì reload catalog và giữ các field còn hợp lệ.
Đổi dataset Xóa selected field và condition của dataset cũ sau khi người dùng xác nhận.
Segment chưa có version Hiển thị “Chưa có lần build dữ liệu”, không coi là lỗi.
Version BUILDING Hiển thị tiến độ và tự refresh có giới hạn.
Version FAILED Hiển thị mã lỗi và thời điểm lỗi; không hiển thị dữ liệu attempt dở.

Kết quả bàn giao

Task 5 - Segment Import Worker build OCS SegmentVersion

Mục tiêu

Đọc NDJSON từ OCS, ghi ClickHouse theo batch và đảm bảo một build có thể retry an toàn trong môi trường nhiều node.

Phạm vi

Dữ liệu thay đổi

New MongoDB: segment_build_job
Alter MongoDB: segment_version.successfulAttemptId
Alter ClickHouse: segment_version_member.import_attempt_id
Alter ClickHouse: segment_version_member.source_data_as_of

Các trường hợp phải xử lý

Trường hợp Xử lý
Hai worker nhận cùng message Chỉ worker claim được lease mới chạy.
Kafka gửi lại message Unique buildKey và trạng thái job làm request idempotent.
Dòng thiếu cả phone và email Ghi invalid count; không insert target member.
Trùng recipient trong một version Dedupe theo recipientKey theo chiến lược đã chốt.
ClickHouse lỗi giữa batch Không ack thành công; attempt bị fail và retry với attemptId mới.
OCS stream kết thúc không có END Attempt thất bại.
Worker chết Lease hết hạn để node khác claim và chạy attempt mới.
Attempt cũ có dữ liệu trong ClickHouse Không được đọc vì khác successfulAttemptId; cleanup sau.

Kết quả bàn giao

Task 6 - CampaignRun orchestration cho Segment OCS

Mục tiêu

Đảm bảo mỗi lần Campaign chạy chờ đúng SegmentVersion, chốt đúng TargetSnapshot và không dispatch dữ liệu cũ.

Phạm vi

Các trường hợp phải xử lý

Trường hợp Xử lý
222 Campaign cùng Segment và cùng sourceDataAsOf Dùng chung một SegmentVersion, mỗi Run có TargetSnapshot riêng.
Cùng Segment nhưng chạy ngày khác Tạo version khác do buildKey khác.
Segment được sửa trong lúc version đang build Build hiện tại giữ snapshot cũ; run sau dùng định nghĩa mới.
Segment bị archive sau khi Run đã PREPARING_TARGET Run đã snapshot có thể tiếp tục theo chính sách; không tạo Run mới.
OCS chậm Run ở PREPARING_TARGET, hiển thị progress; không dispatch version cũ thay thế.
Scheduler chạy trên nhiều node Unique scheduleOccurrenceKey và buildKey ngăn tạo trùng.

Kết quả bàn giao

Task 7 - Cấu hình triển khai, bảo mật và theo dõi vận hành

Mục tiêu

Chuẩn hóa cấu hình để hệ thống chạy ổn định giữa hai vùng LaoAds và OCS, đồng thời đủ dữ liệu để phát hiện và truy vết sự cố.

Phạm vi

Kết quả bàn giao

12. Thứ tự triển khai

[Diagram]

Thứ tự đề xuất:

  1. Chốt engine JDBC và contract OCS trước khi code tích hợp.
  2. Task 1 và Task 2 có thể chạy song song.
  3. Task 3 bắt đầu khi đã chốt contract verify cùng cấu trúc ocs_dataset_configocs_dataset_field_config.
  4. Task 4 có thể ghép mock API sau khi Task 2 chốt response.
  5. Task 5 tích hợp stream thật sau khi Task 1 có môi trường test.
  6. Task 6 chỉ ghép hoàn chỉnh khi Task 5 đảm bảo version READY/FAILED chính xác.
  7. Task 7 đi cùng toàn bộ quá trình, không để đến cuối mới bổ sung log và bảo mật.

13. Mã lỗi cần bổ sung

Tên code có thể điều chỉnh theo registry hiện tại, nhưng phải tách rõ lỗi cấu hình, lỗi OCS và lỗi build:

OCS_DATASET0001  Dataset không tồn tại
OCS_DATASET0002  Dataset không hoạt động
OCS_DATASET0003  Field không thuộc dataset
OCS_DATASET0004  Field không được phép select
OCS_DATASET0005  Field không được phép filter
OCS_DATASET0006  Operator không hợp lệ
OCS_DATASET0007  Thiếu điều kiện bắt buộc
OCS_DATASET0008  Khoảng dữ liệu vượt giới hạn

OCS_VERIFY0001   OCS verify thất bại
OCS_VERIFY0002   Table hoặc column thực tế không tồn tại
OCS_VERIFY0003   Kiểu dữ liệu không khớp
OCS_VERIFY0004   OCS verify timeout

OCS_STREAM0001   Không thể mở stream
OCS_STREAM0002   Stream kết thúc không đầy đủ
OCS_STREAM0003   Dữ liệu stream không đúng contract
OCS_STREAM0004   Hadoop query bị gián đoạn

SEGMENT_BUILD0001  Build job không tồn tại
SEGMENT_BUILD0002  Không thể claim build job
SEGMENT_BUILD0003  ClickHouse insert thất bại
SEGMENT_BUILD0004  Build vượt số lần retry
SEGMENT_BUILD0005  Count đối soát không khớp

Message trả về lấy theo StatusCodeEnum và file đa ngôn ngữ. Không hard-code message nghiệp vụ trong service nếu error code đã có message tương ứng.

14. Kết luận thiết kế

Phương án cuối cùng không dùng Query Template và không dùng GraphQL. Độ linh hoạt được cung cấp bằng danh mục bảng/cột có kiểm soát trong MongoDB và cấu trúc điều kiện cố định.

Ranh giới quan trọng:

Thiết kế này cho phép OCS thay đổi bảng/cột mà không phải sửa giao diện hoặc build lại JAR cho mỗi thay đổi nhỏ, đồng thời vẫn giữ quyền kiểm soát dữ liệu và hiệu suất trong LaoAds.