{
  "repo": "tidbcloud/dataservices",
  "binary": "dataapi_control",
  "binary_path": "cmd/dataapi_control/main.go",
  "dependencies": {
    "DataAPIControlLifecycle.BuildKubernetesConfig": {
      "infrastructure": {
        "kubernetes": {
          "conditions": [
            "Always at startup"
          ],
          "operations": [
            {
              "operation": "LOAD IN-CLUSTER CONFIG",
              "trigger": "Prefer in-cluster credentials for the control-plane client",
              "conditions": [
                "rest.InClusterConfig() succeeds"
              ],
              "implementation": "cfg, err := rest.InClusterConfig()"
            },
            {
              "operation": "LOAD KUBECONFIG",
              "trigger": "Fall back to --master/--kube-config or ~/.kube/config outside the cluster",
              "conditions": [
                "rest.InClusterConfig() returns error"
              ],
              "implementation": "clientcmd.BuildConfigFromFlags(masterURL, kubeconfig)"
            }
          ]
        }
      },
      "services": []
    },
    "DataAPIControlLifecycle.InitControlClients": {
      "infrastructure": {
        "redis": {
          "conditions": [
            "Always at startup"
          ],
          "operations": [
            {
              "operation": "CONNECT",
              "trigger": "Initialize the shared Redis client used by log-query cache and other control-side services",
              "conditions": [
                "Always"
              ],
              "implementation": "redisutil.NewRedisClient(ctx, config)"
            }
          ]
        },
        "dynamodb": {
          "conditions": [
            "Always at startup"
          ],
          "operations": [
            {
              "operation": "CONNECT",
              "trigger": "Initialize DynamoDB-backed user-cluster and cache sync helpers",
              "conditions": [
                "Always"
              ],
              "implementation": "ddb.Init(ctx, config)"
            }
          ]
        },
        "s3": {
          "conditions": [
            "Only when log-query service initialization succeeds"
          ],
          "operations": [
            {
              "operation": "INITIALIZE CLIENT",
              "trigger": "Prepare S3 upload and presign clients for downloaded log archives",
              "conditions": [
                "InitDataAppLogService is reached"
              ],
              "implementation": "InitDataAppLogService -> awsconfig.LoadDefaultConfig(...); s3.NewFromConfig(...); s3.NewPresignClient(...)"
            }
          ]
        }
      },
      "services": [
        {
          "name": "kubernetes-api",
          "target_repo": "kubernetes/kubernetes",
          "target_interface": "controller-runtime client for CustomDomain CRD",
          "operation_type": "k8s_client_init",
          "conditions": [
            "Always at startup"
          ],
          "operations": [
            {
              "operation": "INITIALIZE CLIENT",
              "trigger": "Create the controller-runtime client used by custom-domain handlers",
              "conditions": [
                "BuildKubernetesConfig succeeded"
              ],
              "implementation": "service.InitClient(cfg) -> client.New(cfg, client.Options{Scheme: scheme})"
            }
          ]
        }
      ]
    },
    "DataAPIControlLifecycle.StartDdbCacheWorker": {
      "infrastructure": {},
      "services": [
        {
          "name": "ddb-cache-worker",
          "target_repo": "tidbcloud/dataservices",
          "target_interface": "ddbcacheworker.NewSyncer",
          "operation_type": "background_worker",
          "conditions": [
            "Always at startup"
          ],
          "operations": [
            {
              "operation": "START WORKER LOOP",
              "trigger": "Run background cache sync processing for DDB-backed records",
              "conditions": [
                "Always"
              ],
              "implementation": "worker.NewWorkerManager(ddbcacheworker.NewSyncer(ctx)).Process()"
            }
          ]
        }
      ]
    },
    "DataAPIControlLifecycle.StartTracing": {
      "infrastructure": {},
      "services": []
    },
    "DataAPIControlLifecycle.InitHttpServer": {
      "infrastructure": {
        "dynamodb": {
          "conditions": [
            "Always during control-server bootstrap"
          ],
          "operations": [
            {
              "operation": "CREATE TABLE IF NEEDED",
              "trigger": "Ensure the user-cluster info table exists before loading pending private links",
              "conditions": [
                "Always"
              ],
              "implementation": "ControlServer.bootstrap -> repository_ddb.CreateUserClusterInfoTableIfNotExist(ctx, s.ddbCli)"
            },
            {
              "operation": "QUERY",
              "trigger": "Load pending private-link records to resume reconciliation after restart",
              "conditions": [
                "Always"
              ],
              "implementation": "ControlServer.bootstrap -> repository_ddb.QueryUserClusterInfoWithPendingPrivateLink(ctx, s.ddbCli)"
            }
          ]
        }
      },
      "services": [
        {
          "name": "aws-ec2",
          "target_repo": "aws/aws-sdk-go-v2",
          "target_interface": "EC2 client for VPC endpoint lifecycle",
          "operation_type": "sdk_init",
          "conditions": [
            "Always during control-server bootstrap"
          ],
          "operations": [
            {
              "operation": "INITIALIZE EC2 CLIENT",
              "trigger": "Prepare the EC2 client used by private-link create/delete/reconcile operations",
              "conditions": [
                "Always"
              ],
              "implementation": "InitControlServer -> ddb.LoadAwsConfig(...); ec2.NewFromConfig(*awsConfig)"
            }
          ]
        }
      ]
    },
    "DataAPIControlLifecycle.StartHttpServer": {
      "infrastructure": {
        "tcp": {
          "conditions": [
            "Always at startup"
          ],
          "operations": [
            {
              "operation": "LISTEN",
              "trigger": "Expose the inner HTTP control endpoints",
              "conditions": [
                "Always"
              ],
              "implementation": "startupHttp -> ginEng.Run(addr)"
            }
          ]
        }
      },
      "services": []
    },
    "DataAPIControlLifecycle.StartMetricsServer": {
      "infrastructure": {},
      "services": []
    },
    "HTTP GET /healthz": {
      "infrastructure": {},
      "services": []
    },
    "HTTP POST /inner/dataservice/projects/:project_id/clusters/:cluster_id/privatelink/create": {
      "infrastructure": {
        "dynamodb": {
          "conditions": [
            "Only after request parsing succeeds"
          ],
          "operations": [
            {
              "operation": "GET / UPDATE USER-CLUSTER INFO",
              "trigger": "Check current private-link state and persist the newly created endpoint info",
              "conditions": [
                "GlobalControlServer.CreatePrivateLink is reached"
              ],
              "implementation": "CreatePrivateLink -> repository_ddb.GetUserClusterInfo / PutUserClusterInfo"
            }
          ]
        }
      },
      "services": [
        {
          "name": "aws-ec2",
          "target_repo": "aws/aws-sdk-go-v2",
          "target_interface": "CreateVpcEndpoint / DescribeVpcEndpoint / EnablePrivateLinkDNS / GetPrivateLinkDNS",
          "operation_type": "aws_api",
          "conditions": [
            "Only after JSON body parsing succeeds and the cluster has no completed private link yet"
          ],
          "operations": [
            {
              "operation": "CREATE VPC ENDPOINT",
              "trigger": "Create the PrivateLink endpoint and start background reconciliation",
              "conditions": [
                "parseParamFromBody succeeded",
                "clusterInfo.EndpointId is empty or missing DNS"
              ],
              "implementation": "HandleCreatePrivateLink -> GlobalControlServer.CreatePrivateLink -> CreateVpcEndpoint(...); createCh <- info"
            }
          ]
        }
      ]
    },
    "HTTP POST /inner/dataservice/projects/:project_id/clusters/:cluster_id/privatelink/delete": {
      "infrastructure": {
        "dynamodb": {
          "conditions": [
            "Only when the control server reaches the delete path"
          ],
          "operations": [
            {
              "operation": "DELETE OR CLEAR USER-CLUSTER INFO",
              "trigger": "Remove endpoint id/private DNS or delete the entire cluster record once deletion is scheduled",
              "conditions": [
                "GlobalControlServer.DeletePrivateLink succeeded and the background loop processes deleteCh"
              ],
              "implementation": "DeletePrivateLink -> deleteCh <- info; MaintainPrivateLinkLoop -> repository_ddb.DeleteUserClusterInfo / PutUserClusterInfo"
            }
          ]
        }
      },
      "services": [
        {
          "name": "aws-ec2",
          "target_repo": "aws/aws-sdk-go-v2",
          "target_interface": "DeleteVpcEndpoint",
          "operation_type": "aws_api",
          "conditions": [
            "Only when the cluster has an endpoint id to delete"
          ],
          "operations": [
            {
              "operation": "DELETE VPC ENDPOINT",
              "trigger": "Delete the PrivateLink endpoint and hand cleanup to the background loop",
              "conditions": [
                "GlobalControlServer.DeletePrivateLink is reached"
              ],
              "implementation": "HandleDeletePrivateLink -> GlobalControlServer.DeletePrivateLink -> DeleteVpcEndpoint(ctx, cli, endpointId)"
            }
          ]
        }
      ]
    },
    "HTTP POST /inner/dataservice/apps/:app_id/custom-domains": {
      "infrastructure": {},
      "services": [
        {
          "name": "kubernetes-api",
          "target_repo": "kubernetes/kubernetes",
          "target_interface": "CustomDomain CR create/get",
          "operation_type": "k8s_crud",
          "conditions": [
            "Only after JSON body parsing succeeds"
          ],
          "operations": [
            {
              "operation": "GET OR CREATE CUSTOMDOMAIN",
              "trigger": "Return existing state or create the CustomDomain CR for the requested host",
              "conditions": [
                "parseParamFromBody succeeded"
              ],
              "implementation": "HandleCreateCustomDomain -> service.KubeCreateCustomDomain -> kubeClient.Get / kubeClient.Create"
            }
          ]
        }
      ]
    },
    "HTTP POST /inner/dataservice/apps/:app_id/custom-domains/:host/getState": {
      "infrastructure": {},
      "services": [
        {
          "name": "kubernetes-api",
          "target_repo": "kubernetes/kubernetes",
          "target_interface": "CustomDomain CR get",
          "operation_type": "k8s_get",
          "conditions": [
            "Always after host path param is present"
          ],
          "operations": [
            {
              "operation": "GET CUSTOMDOMAIN",
              "trigger": "Read the CR state for the requested host",
              "conditions": [
                "host path parameter is non-empty"
              ],
              "implementation": "HandleGetCustomDomain -> service.KubeGetCustomDomain -> kubeClient.Get"
            }
          ]
        }
      ]
    },
    "HTTP POST /inner/dataservice/apps/:app_id/custom-domains/:host/delete": {
      "infrastructure": {},
      "services": [
        {
          "name": "kubernetes-api",
          "target_repo": "kubernetes/kubernetes",
          "target_interface": "CustomDomain CR delete",
          "operation_type": "k8s_delete",
          "conditions": [
            "Always after host path param is present"
          ],
          "operations": [
            {
              "operation": "DELETE CUSTOMDOMAIN",
              "trigger": "Delete the CR for the requested host",
              "conditions": [
                "host path parameter is non-empty"
              ],
              "implementation": "HandleDeleteCustomDomain -> service.KubeDeleteCustomDomain -> kubeClient.Delete"
            }
          ]
        }
      ]
    },
    "HTTP POST /inner/dataservice/apps/:app_id/dataapp-log/query": {
      "infrastructure": {
        "redis": {
          "conditions": [
            "Only when query chunk cache/query-id bookkeeping is used"
          ],
          "operations": [
            {
              "operation": "READ / WRITE CACHE",
              "trigger": "Cache chunked Loki query results and query metadata for pagination",
              "conditions": [
                "ExecuteQuery is reached"
              ],
              "implementation": "ExecuteQuery -> storeQuery / tryFetchFromCache / writeChunksToCache using globalClient.cacheMaster"
            }
          ]
        }
      },
      "services": [
        {
          "name": "loki",
          "target_repo": "grafana/loki",
          "target_interface": "GET /loki/api/v1/query_range",
          "operation_type": "http_get",
          "conditions": [
            "Only when cache miss or query refill reaches Loki"
          ],
          "operations": [
            {
              "operation": "QUERY RANGE",
              "trigger": "Fetch execution log streams from Loki",
              "conditions": [
                "ExecuteQuery or fillCache path reaches queryLoki"
              ],
              "implementation": "queryLoki -> http.NewRequest(http.MethodGet, queryUrl.String(), nil); globalClient.httpClient.Do(req)"
            }
          ]
        }
      ]
    },
    "HTTP POST /inner/dataservice/apps/:app_id/dataapp-log/download": {
      "infrastructure": {
        "filesystem": {
          "conditions": [
            "Only when background download worker reaches file generation"
          ],
          "operations": [
            {
              "operation": "WRITE ZIP FILE",
              "trigger": "Build the downloadable log archive on local disk before uploading it",
              "conditions": [
                "ExecuteDownload spawned downloadAndWriteLogWorkRoutine"
              ],
              "implementation": "downloadAndWriteLog -> os.Create(zipfilename); zip.NewWriter(...)"
            }
          ]
        }
      },
      "services": [
        {
          "name": "loki",
          "target_repo": "grafana/loki",
          "target_interface": "GET /loki/api/v1/query_range",
          "operation_type": "http_get",
          "conditions": [
            "Only when the background downloader fetches log chunks"
          ],
          "operations": [
            {
              "operation": "QUERY RANGE",
              "trigger": "Fetch log chunks to populate the downloadable archive",
              "conditions": [
                "ExecuteDownload is reached"
              ],
              "implementation": "ExecuteDownload -> downloadAndWriteLogWorkRoutine -> downloadAndWriteLog -> queryLoki"
            }
          ]
        },
        {
          "name": "s3",
          "target_repo": "aws/aws-sdk-go-v2",
          "target_interface": "S3 uploader + presign",
          "operation_type": "s3_put",
          "conditions": [
            "Only after the zip file is written successfully"
          ],
          "operations": [
            {
              "operation": "UPLOAD FILE",
              "trigger": "Upload the generated archive and build a downloadable URL",
              "conditions": [
                "downloadAndWriteLog completed successfully"
              ],
              "implementation": "uploadFile -> manager.NewUploader(c.S3Client).Upload(...); c.S3PresignClient.PresignGetObject(...)"
            }
          ]
        }
      ]
    },
    "HTTP POST /inner/dataservice/apps/:app_id/dataapp-metrics/query": {
      "infrastructure": {},
      "services": [
        {
          "name": "prometheus",
          "target_repo": "prometheus/prometheus",
          "target_interface": "GET /api/v1/query or /api/v1/query_range",
          "operation_type": "http_get",
          "conditions": [
            "Only when MetricsQuery dispatches to Prometheus-backed paths"
          ],
          "operations": [
            {
              "operation": "QUERY PROMETHEUS",
              "trigger": "Fetch request/error/latency metrics from Prometheus",
              "conditions": [
                "MetricsQuery chooses a Prometheus-backed helper"
              ],
              "implementation": "MetricsQuery -> QueryDataapp* -> doRangeQueryProm/doInstantQueryProm -> doPromQuery"
            }
          ]
        },
        {
          "name": "loki",
          "target_repo": "grafana/loki",
          "target_interface": "GET /loki/api/v1/query",
          "operation_type": "http_get",
          "conditions": [
            "Only when MetricsQuery dispatches to Loki-backed top-k helpers"
          ],
          "operations": [
            {
              "operation": "QUERY LOKI METRIC API",
              "trigger": "Fetch top-k endpoint metrics from Loki when the configured range is short",
              "conditions": [
                "MetricsQuery chooses QueryDataappTopK*LokiVer"
              ],
              "implementation": "QueryDataappTopK*LokiVer -> doInstantQueryLokiMetric(...)"
            }
          ]
        }
      ]
    }
  }
}
