Merge pull request 'feature/icecheng/compute_unit' (#96) from feature/icecheng/compute_unit into dev
Reviewed-on: freeleaps/freeleaps-service-hub#96
This commit is contained in:
commit
512e418670
31
CHANGELOG.md
31
CHANGELOG.md
@ -1,3 +1,34 @@
|
|||||||
|
## [1.14.2](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/compare/v1.14.1...v1.14.2) (2025-10-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* change middleware to avoid Beanie init ([1823a33](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/commit/1823a33d45abdf2076a2eee5d899e50d8c72993f))
|
||||||
|
* change the authentication service model layer from Beanie to direct MongoDB ([a936f89](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/commit/a936f8942644d14b5af91bd5d2e372e12cbc2043))
|
||||||
|
* fix the id wrong type issue ([f57cfb2](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/commit/f57cfb2d3accf7ee8817c8fcdc2bd4a1aa236aa1))
|
||||||
|
* fix the invalid id format issues ([ba63e1b](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/commit/ba63e1b7a8835e9f898d503a01ec837da83e5072))
|
||||||
|
|
||||||
|
## [1.14.1](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/compare/v1.14.0...v1.14.1) (2025-10-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* change name to more general ones ([2036c4b](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/commit/2036c4b9ae5bd9d1d7b00daead0e047b6d9e4ebb))
|
||||||
|
* create apis for magicleaps password things avoiding calling code depot ([497e608](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/commit/497e6080a3cb703621ae9b17f3ca2b0e1c4c3672))
|
||||||
|
* fix the small issue of user_id not found ([6a207c7](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/commit/6a207c7e79abc9400724caa3ee873f1e579deeb0))
|
||||||
|
|
||||||
|
# [1.14.0](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/compare/v1.13.0...v1.14.0) (2025-10-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** add extrnal auth introspect api interface ([7a83237](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/commit/7a832371c7790c77d060344a4141c1dc5c435333))
|
||||||
|
* **env:** add env config ([6bbaaae](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/commit/6bbaaae30f2649e787aeddd62779301447ae537b))
|
||||||
|
* **format:** modify the log format ([81f2a21](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/commit/81f2a21f6bb8390f851316a05809041f60b3ea4d))
|
||||||
|
* **middleware:** add middleware for authentication ([80a6beb](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/commit/80a6beb1ed36d94f3024a80045cbf82a1d0a8844))
|
||||||
|
* **middleware:** modify database to tolerate the tenant cache ([7844906](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/commit/7844906cfe64b1b2f2d8ae29b064213385c6ce98))
|
||||||
|
* **model:** add usage_log_doc ([5f18212](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/commit/5f18212343f93eebdec57498a56f60a8065b2501))
|
||||||
|
|
||||||
# [1.13.0](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/compare/v1.12.1...v1.13.0) (2025-09-30)
|
# [1.13.0](https://gitea.freeleaps.mathmast.com/freeleaps/freeleaps-service-hub/compare/v1.12.1...v1.13.0) (2025-09-30)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -25,6 +25,7 @@ class Deployment(Document):
|
|||||||
deployment_git_url: str
|
deployment_git_url: str
|
||||||
deployment_git_sha256: str
|
deployment_git_sha256: str
|
||||||
deployment_reason: str
|
deployment_reason: str
|
||||||
|
compute_unit: Optional[int] = None # None for old data
|
||||||
deployment_app_url: str = "" # URL to access the deployed application, keep it empty to be filled later
|
deployment_app_url: str = "" # URL to access the deployed application, keep it empty to be filled later
|
||||||
|
|
||||||
deployed_by: str
|
deployed_by: str
|
||||||
@ -46,6 +47,7 @@ class InitDeploymentRequest(BaseModel):
|
|||||||
user_id: str
|
user_id: str
|
||||||
reason: str = "not provided"
|
reason: str = "not provided"
|
||||||
ttl_hours: int = -1
|
ttl_hours: int = -1
|
||||||
|
compute_unit: Optional[int] = 0
|
||||||
|
|
||||||
class CheckDeploymentStatusRequest(BaseModel):
|
class CheckDeploymentStatusRequest(BaseModel):
|
||||||
product_id: str
|
product_id: str
|
||||||
@ -85,4 +87,5 @@ class DevOpsReconcileRequest(BaseModel):
|
|||||||
commit_sha256: Optional[str] = None
|
commit_sha256: Optional[str] = None
|
||||||
target_env: Literal["alpha", "prod"]
|
target_env: Literal["alpha", "prod"]
|
||||||
ttl_control: bool = False
|
ttl_control: bool = False
|
||||||
ttl: int = 10800
|
ttl: int = 10800
|
||||||
|
compute_unit: Optional[int] = 0
|
||||||
@ -58,6 +58,7 @@ class DeploymentService:
|
|||||||
deployed_by = request.user_id,
|
deployed_by = request.user_id,
|
||||||
created_at = datetime.now(),
|
created_at = datetime.now(),
|
||||||
updated_at = datetime.now(),
|
updated_at = datetime.now(),
|
||||||
|
compute_unit = request.compute_unit,
|
||||||
)
|
)
|
||||||
|
|
||||||
await self._start_deployment(deployment)
|
await self._start_deployment(deployment)
|
||||||
@ -182,6 +183,7 @@ class DeploymentService:
|
|||||||
ttl_control=deployment.deployment_ttl_hours > 0,
|
ttl_control=deployment.deployment_ttl_hours > 0,
|
||||||
ttl=10800 if deployment.deployment_ttl_hours < 0 else deployment.deployment_ttl_hours * 60 * 60,
|
ttl=10800 if deployment.deployment_ttl_hours < 0 else deployment.deployment_ttl_hours * 60 * 60,
|
||||||
commit_sha256=deployment.deployment_git_sha256,
|
commit_sha256=deployment.deployment_git_sha256,
|
||||||
|
compute_unit=deployment.compute_unit
|
||||||
)
|
)
|
||||||
# send request to reoncile service
|
# send request to reoncile service
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
|
|||||||
@ -41,6 +41,70 @@ class StarRocksMetricsService:
|
|||||||
AND product_id = %s
|
AND product_id = %s
|
||||||
ORDER BY date ASC
|
ORDER BY date ASC
|
||||||
""",
|
""",
|
||||||
|
"dcr": """
|
||||||
|
SELECT
|
||||||
|
date,
|
||||||
|
product_id,
|
||||||
|
value,
|
||||||
|
updated_date
|
||||||
|
FROM dws_dcr
|
||||||
|
WHERE date >= %s
|
||||||
|
AND date < %s
|
||||||
|
AND product_id = %s
|
||||||
|
ORDER BY date ASC
|
||||||
|
""",
|
||||||
|
"mrar": """
|
||||||
|
SELECT
|
||||||
|
date,
|
||||||
|
product_id,
|
||||||
|
CASE
|
||||||
|
WHEN monthly_requests = 0 THEN 0.0
|
||||||
|
ELSE (monthly_accepted_requests * 1.0) / monthly_requests
|
||||||
|
END AS value,
|
||||||
|
updated_date
|
||||||
|
FROM dws_mrar
|
||||||
|
WHERE date >= %s
|
||||||
|
AND date < %s
|
||||||
|
AND product_id = %s
|
||||||
|
ORDER BY date ASC
|
||||||
|
""",
|
||||||
|
"trar": """
|
||||||
|
SELECT
|
||||||
|
product_id,
|
||||||
|
CASE
|
||||||
|
WHEN total_requests = 0 THEN 0.0
|
||||||
|
ELSE (total_accepted_requests * 1.0) / total_requests
|
||||||
|
END AS value,
|
||||||
|
updated_date
|
||||||
|
FROM dws_trar
|
||||||
|
WHERE product_id = %s
|
||||||
|
""",
|
||||||
|
"mrqr": """
|
||||||
|
SELECT
|
||||||
|
date,
|
||||||
|
product_id,
|
||||||
|
CASE
|
||||||
|
WHEN monthly_requests = 0 THEN 0.0
|
||||||
|
ELSE (monthly_quoted_requests * 1.0) / monthly_requests
|
||||||
|
END AS value,
|
||||||
|
updated_date
|
||||||
|
FROM dws_mrqr
|
||||||
|
WHERE date >= %s
|
||||||
|
AND date < %s
|
||||||
|
AND product_id = %s
|
||||||
|
ORDER BY date ASC
|
||||||
|
""",
|
||||||
|
"trqr": """
|
||||||
|
SELECT
|
||||||
|
product_id,
|
||||||
|
CASE
|
||||||
|
WHEN total_requests = 0 THEN 0.0
|
||||||
|
ELSE (total_quoted_requests * 1.0) / total_requests
|
||||||
|
END AS value,
|
||||||
|
updated_date
|
||||||
|
FROM dws_trqr
|
||||||
|
WHERE product_id = %s
|
||||||
|
""",
|
||||||
},
|
},
|
||||||
"magicleaps": {
|
"magicleaps": {
|
||||||
|
|
||||||
@ -92,9 +156,9 @@ class StarRocksMetricsService:
|
|||||||
self,
|
self,
|
||||||
product_id: str,
|
product_id: str,
|
||||||
metric_name: str,
|
metric_name: str,
|
||||||
step: str,
|
step: Optional[str],
|
||||||
start_date: Union[str, date],
|
start_date: Optional[Union[str, date]],
|
||||||
end_date: Union[str, date]
|
end_date: Optional[Union[str, date]]
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Query metric data for a specific date range.
|
Query metric data for a specific date range.
|
||||||
@ -140,6 +204,17 @@ class StarRocksMetricsService:
|
|||||||
await self.module_logger.log_error(error_msg)
|
await self.module_logger.log_error(error_msg)
|
||||||
raise HTTPException(status_code=404, detail=error_msg)
|
raise HTTPException(status_code=404, detail=error_msg)
|
||||||
|
|
||||||
|
# Check if metric need time params
|
||||||
|
# Starting with "t" indicates a query for the total count since the very first record.
|
||||||
|
# NOTE: This determination logic is subject to future changes.
|
||||||
|
if (start_date is None) or (end_date is None) or (step is None):
|
||||||
|
if metric_name.startswith('t'):
|
||||||
|
return await self._query_metric_by_product_id(product_id, metric_name)
|
||||||
|
else:
|
||||||
|
error_msg = f"Metric '{metric_name}' should be queried by start date, end date and step."
|
||||||
|
await self.module_logger.log_error(error_msg)
|
||||||
|
raise HTTPException(status_code=404, detail=error_msg)
|
||||||
|
|
||||||
# Parse date strings if they are strings
|
# Parse date strings if they are strings
|
||||||
if isinstance(start_date, str):
|
if isinstance(start_date, str):
|
||||||
try:
|
try:
|
||||||
@ -284,7 +359,7 @@ class StarRocksMetricsService:
|
|||||||
|
|
||||||
result_dict[date_str] = {
|
result_dict[date_str] = {
|
||||||
"date": date_str,
|
"date": date_str,
|
||||||
"value": int(value) if value is not None else 0,
|
"value": value if value is not None else 0,
|
||||||
"metric": metric_name,
|
"metric": metric_name,
|
||||||
"labels": labels
|
"labels": labels
|
||||||
}
|
}
|
||||||
@ -373,4 +448,67 @@ class StarRocksMetricsService:
|
|||||||
"metric_name": metric_name,
|
"metric_name": metric_name,
|
||||||
"sql_query": self.METRIC_SQL_MAP[product_id][metric_name].strip(),
|
"sql_query": self.METRIC_SQL_MAP[product_id][metric_name].strip(),
|
||||||
"description": f"{metric_name} count from StarRocks table dws_{metric_name}"
|
"description": f"{metric_name} count from StarRocks table dws_{metric_name}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async def _query_metric_by_product_id(self, product_id: str, metric_name: str) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Query metric not suitable for date range (e.g. data related to calculating total records).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
product_id: Product ID to identify which product's metrics to query
|
||||||
|
metric_name: Name of the metric to query
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of dictionaries with 'product_id' key.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: If StarRocks query fails
|
||||||
|
|
||||||
|
Example:
|
||||||
|
result = await service.query_metric_by_time_range(
|
||||||
|
"freeleaps",
|
||||||
|
"total_request_quoted_rate",
|
||||||
|
)
|
||||||
|
# Returns: [{"date": "freeleaps", "value": 45, "labels": {...}},]
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Get the SQL query for the metric
|
||||||
|
sql_query = self.METRIC_SQL_MAP[product_id][metric_name]
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self.module_logger.log_info(
|
||||||
|
f"Querying metric '{metric_name}' from product '{product_id}'")
|
||||||
|
|
||||||
|
# Execute the query
|
||||||
|
result = await self.starrocks_client.execute_query(
|
||||||
|
query=sql_query,
|
||||||
|
params=(product_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Parse the result and format it
|
||||||
|
for row in result:
|
||||||
|
# Get the value
|
||||||
|
value = row.get("value", 0)
|
||||||
|
if value is None:
|
||||||
|
value = 0
|
||||||
|
|
||||||
|
# Create labels dictionary
|
||||||
|
labels = {
|
||||||
|
"product_id": row.get("product_id", product_id),
|
||||||
|
"metric_type": metric_name,
|
||||||
|
}
|
||||||
|
result_dict = []
|
||||||
|
result_dict.append({
|
||||||
|
"date": None,
|
||||||
|
"value": value if value is not None else 0,
|
||||||
|
"metric": metric_name,
|
||||||
|
"labels": labels
|
||||||
|
})
|
||||||
|
|
||||||
|
await self.module_logger.log_info(
|
||||||
|
f"Successfully queried metric '{metric_name}'")
|
||||||
|
return result_dict
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await self.module_logger.log_error(f"Failed to query metric '{metric_name}': {e}")
|
||||||
|
raise
|
||||||
@ -9,7 +9,7 @@ from backend.services.starrocks_metrics_service import StarRocksMetricsService
|
|||||||
|
|
||||||
class MetricDataPoint(BaseModel):
|
class MetricDataPoint(BaseModel):
|
||||||
"""Single data point in metric time series."""
|
"""Single data point in metric time series."""
|
||||||
date: str = Field(..., description="Date in YYYY-MM-DD format")
|
date: Optional[str] = Field(None, description="Date in YYYY-MM-DD format")
|
||||||
value: Union[int, float] = Field(..., description="Metric value")
|
value: Union[int, float] = Field(..., description="Metric value")
|
||||||
labels: Dict[str, Any] = Field(default_factory=dict, description="Metric labels")
|
labels: Dict[str, Any] = Field(default_factory=dict, description="Metric labels")
|
||||||
|
|
||||||
@ -19,17 +19,16 @@ class MetricTimeSeriesResponse(BaseModel):
|
|||||||
metric_name: str = Field(..., description="Name of the queried metric")
|
metric_name: str = Field(..., description="Name of the queried metric")
|
||||||
data_points: List[MetricDataPoint] = Field(..., description="List of data points")
|
data_points: List[MetricDataPoint] = Field(..., description="List of data points")
|
||||||
total_points: int = Field(..., description="Total number of data points")
|
total_points: int = Field(..., description="Total number of data points")
|
||||||
time_range: Dict[str, str] = Field(..., description="Start and end date of the query")
|
time_range: Dict[Optional[str], Optional[str]] = Field([None, None], description="Start and end date of the query")
|
||||||
|
|
||||||
|
|
||||||
class MetricQueryRequest(BaseModel):
|
class MetricQueryRequest(BaseModel):
|
||||||
"""Request model for metric query."""
|
"""Request model for metric query by time range."""
|
||||||
product_id: str = Field(..., description="Product ID to identify which product's data to query")
|
product_id: str = Field(..., description="Product ID to identify which product's data to query")
|
||||||
metric_name: str = Field(..., description="Name of the metric to query")
|
metric_name: str = Field(..., description="Name of the metric to query")
|
||||||
step: str = Field(..., description="Aggregation step, e.g., 1d or 1m")
|
step: Optional[str] = Field(None, description="Aggregation step, e.g., 1d or 1m")
|
||||||
start_date: str = Field(..., description="Start date in YYYY-MM-DD HH:MM:SS format")
|
start_date: Optional[str] = Field(None, description="Start date in YYYY-MM-DD HH:MM:SS format")
|
||||||
end_date: str = Field(..., description="End date in YYYY-MM-DD HH:MM:SS format")
|
end_date: Optional[str] = Field(None, description="End date in YYYY-MM-DD HH:MM:SS format")
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@ -72,11 +71,11 @@ async def metrics_query(
|
|||||||
],
|
],
|
||||||
total_points=len(data_points),
|
total_points=len(data_points),
|
||||||
time_range={
|
time_range={
|
||||||
"start": request.start_date,
|
"start": request.start_date if request.start_date else None,
|
||||||
"end": request.end_date
|
"end": request.end_date if request.end_date else None
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
await module_logger.log_info(
|
await module_logger.log_info(
|
||||||
f"Successfully queried metric '{request.metric_name}' with {len(data_points)} data points")
|
f"Successfully queried metric '{request.metric_name}' with {len(data_points)} data points")
|
||||||
return response
|
return response
|
||||||
Loading…
Reference in New Issue
Block a user