Skip to content

Commit f513c72

Browse files
adding tags to rut
1 parent 0d090eb commit f513c72

File tree

8 files changed

+147
-45
lines changed

8 files changed

+147
-45
lines changed

packages/models-library/src/models_library/rabbitmq_messages.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ class RabbitResourceTrackingProjectSyncMessage(RabbitMessageBase):
201201

202202
project_id: ProjectID
203203
project_name: str | None = None
204-
project_tags_names: list[str] | None = None
204+
project_tags: list[tuple[int, str]] | None = None
205205
created_at: datetime.datetime = Field(
206206
default_factory=lambda: arrow.utcnow().datetime,
207207
description="message creation datetime",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""introducing rut project metadata
2+
3+
Revision ID: da145a5fe27b
4+
Revises: 8bfe65a5e294
5+
Create Date: 2024-11-12 16:03:41.595377+00:00
6+
7+
"""
8+
import sqlalchemy as sa
9+
from alembic import op
10+
from sqlalchemy.dialects import postgresql
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "da145a5fe27b"
14+
down_revision = "8bfe65a5e294"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.create_table(
22+
"resource_tracker_project_metadata",
23+
sa.Column("project_id", sa.String(), nullable=False),
24+
sa.Column("project_name", sa.String(), nullable=False),
25+
sa.Column(
26+
"project_tags",
27+
postgresql.JSONB(astext_type=sa.Text()),
28+
server_default=sa.text("'{}'::jsonb"),
29+
nullable=False,
30+
),
31+
sa.Column(
32+
"modified",
33+
sa.DateTime(timezone=True),
34+
server_default=sa.text("now()"),
35+
nullable=False,
36+
),
37+
sa.PrimaryKeyConstraint("project_id"),
38+
)
39+
op.create_foreign_key(
40+
"fk_resource_tracker_service_runs_project_metadata_project_id",
41+
"resource_tracker_service_runs",
42+
"resource_tracker_project_metadata",
43+
["project_id"],
44+
["project_id"],
45+
onupdate="CASCADE",
46+
ondelete="RESTRICT",
47+
)
48+
# ### end Alembic commands ###
49+
50+
51+
def downgrade():
52+
# ### commands auto generated by Alembic - please adjust! ###
53+
op.drop_constraint(
54+
"fk_resource_tracker_service_runs_project_metadata_project_id",
55+
"resource_tracker_service_runs",
56+
type_="foreignkey",
57+
)
58+
op.drop_table("resource_tracker_project_metadata")
59+
# ### end Alembic commands ###
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
""" resource_tracker_service_runs table
2+
"""
3+
4+
import sqlalchemy as sa
5+
from sqlalchemy.dialects.postgresql import JSONB
6+
7+
from ._common import column_modified_datetime
8+
from .base import metadata
9+
10+
resource_tracker_project_metadata = sa.Table(
11+
"resource_tracker_project_metadata",
12+
metadata,
13+
sa.Column(
14+
"project_id", # UUID
15+
sa.String,
16+
nullable=False,
17+
primary_key=True,
18+
),
19+
sa.Column(
20+
"project_name",
21+
sa.String,
22+
nullable=False,
23+
doc="we want to store the project name for tracking/billing purposes and be sure it stays there even when the project is deleted (that's also reason why we do not introduce foreign key)",
24+
),
25+
sa.Column(
26+
"project_tags",
27+
JSONB,
28+
nullable=False,
29+
server_default=sa.text("'{}'::jsonb"),
30+
doc="we want to store the project tags for tracking/billing purposes and be sure it stays there even when the project is deleted (that's also reason why we do not introduce foreign key)",
31+
),
32+
column_modified_datetime(timezone=True),
33+
)

packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_service_runs.py

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ class ResourceTrackerServiceRunStatus(str, enum.Enum):
9898
sa.Column(
9999
"project_id", # UUID
100100
sa.String,
101+
sa.ForeignKey(
102+
"resource_tracker_project_metadata.project_id",
103+
name="fk_resource_tracker_service_runs_project_metadata_project_id",
104+
onupdate="CASCADE",
105+
ondelete="RESTRICT",
106+
),
101107
nullable=False,
102108
doc="We want to store the project id for tracking/billing purposes and be sure it stays there even when the project is deleted (that's also reason why we do not introduce foreign key)",
103109
),
@@ -236,30 +242,3 @@ class ResourceTrackerServiceRunStatus(str, enum.Enum):
236242
== ResourceTrackerServiceRunStatus.RUNNING
237243
),
238244
)
239-
240-
241-
resource_tracker_project_metadata = sa.Table(
242-
"resource_tracker_project_metadata",
243-
metadata,
244-
sa.Column(
245-
"project_id", # UUID
246-
sa.String,
247-
nullable=False,
248-
doc="We want to store the project id for tracking/billing purposes and be sure it stays there even when the project is deleted (that's also reason why we do not introduce foreign key)",
249-
primary_key=True,
250-
),
251-
# sa.Column(
252-
# "project_name",
253-
# sa.String,
254-
# nullable=False,
255-
# doc="we want to store the project name for tracking/billing purposes and be sure it stays there even when the project is deleted (that's also reason why we do not introduce foreign key)",
256-
# ),
257-
sa.Column(
258-
"project_tags_names",
259-
JSONB,
260-
nullable=False,
261-
server_default=sa.text("'{}'::jsonb"),
262-
doc="we want to store the project name for tracking/billing purposes and be sure it stays there even when the project is deleted (that's also reason why we do not introduce foreign key)",
263-
),
264-
column_modified_datetime(timezone=True),
265-
)

services/resource-usage-tracker/src/simcore_service_resource_usage_tracker/services/modules/db/repositories/resource_tracker.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
)
1010
from models_library.api_schemas_storage import S3BucketName
1111
from models_library.products import ProductName
12+
from models_library.projects import ProjectID
1213
from models_library.resource_tracker import (
1314
CreditClassification,
1415
CreditTransactionId,
@@ -43,10 +44,12 @@
4344
from simcore_postgres_database.models.resource_tracker_pricing_units import (
4445
resource_tracker_pricing_units,
4546
)
47+
from simcore_postgres_database.models.resource_tracker_project_metadata import (
48+
resource_tracker_project_metadata,
49+
)
4650
from simcore_postgres_database.models.resource_tracker_service_runs import (
4751
resource_tracker_service_runs,
4852
)
49-
from simcore_service_resource_usage_tracker.services.utils import ProjectID
5053
from sqlalchemy.dialects.postgresql import ARRAY, INTEGER
5154

5255
from .....exceptions.errors import (
@@ -204,31 +207,27 @@ async def insert_rut_project_metadata(
204207
self,
205208
project_id: ProjectID,
206209
project_name: str,
207-
project_tags_names: list[str],
210+
project_tags_db: dict[str, dict[str, str]],
208211
) -> None:
209212
async with self.db_engine.begin() as conn:
210-
insert_stmt = (
211-
resource_tracker_service_runs.insert()
212-
.values(
213-
project_id=f"{project_id}",
214-
project_name=project_name,
215-
project_tags_names=project_tags_names,
216-
modified=sa.func.now(),
217-
)
218-
.returning(resource_tracker_credit_transactions.c.transaction_id)
213+
insert_stmt = resource_tracker_project_metadata.insert().values(
214+
project_id=f"{project_id}",
215+
project_name=project_name,
216+
project_tags=project_tags_db,
217+
modified=sa.func.now(),
219218
)
220219
await conn.execute(insert_stmt)
221220

222221
async def update_rut_project_metadata(
223222
self,
224223
project_id: ProjectID,
225224
project_name: str | None = None,
226-
project_tags_names: list[str] | None = None,
225+
project_tags: list[str] | None = None,
227226
) -> None:
228227

229228
_update_data = {
230229
"project_name": project_name,
231-
"project_tags_names": project_tags_names,
230+
"project_tags": project_tags,
232231
}
233232
_update_data_clean = {k: v for k, v in _update_data.items() if v is not None}
234233

services/resource-usage-tracker/src/simcore_service_resource_usage_tracker/services/process_message_running_service.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,15 @@ async def _process_start_event(
9595
)
9696
pricing_unit_cost = pricing_unit_cost_db.cost_per_unit
9797

98+
project_tags_db: dict[str, dict[str, str]] = {}
99+
for tag in msg.project_tags:
100+
project_tags_db[f"{tag[0]}"] = {"name": tag[1]}
101+
await resource_tracker_repo.insert_rut_project_metadata(
102+
project_id=msg.project_id,
103+
project_name=msg.project_name,
104+
project_tags_db=jsonable_encoder(project_tags_db),
105+
)
106+
98107
create_service_run = ServiceRunCreate(
99108
product_name=msg.product_name,
100109
service_run_id=msg.service_run_id,
@@ -126,9 +135,6 @@ async def _process_start_event(
126135
last_heartbeat_at=msg.created_at,
127136
)
128137
service_run_id = await resource_tracker_repo.create_service_run(create_service_run)
129-
await resource_tracker_repo.insert_rut_project_metadata(
130-
project_id=msg.project_id, project_name=msg.project_name, project_tags_names=[]
131-
)
132138

133139
if msg.wallet_id and msg.wallet_name:
134140
transaction_create = CreditTransactionCreate(

services/resource-usage-tracker/tests/unit/with_dbs/conftest.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from asgi_lifespan import LifespanManager
1515
from faker import Faker
1616
from fastapi import FastAPI
17+
from models_library.projects import ProjectID
1718
from models_library.rabbitmq_messages import (
1819
RabbitResourceTrackingHeartbeatMessage,
1920
RabbitResourceTrackingMessageType,
@@ -26,6 +27,9 @@
2627
from simcore_postgres_database.models.resource_tracker_credit_transactions import (
2728
resource_tracker_credit_transactions,
2829
)
30+
from simcore_postgres_database.models.resource_tracker_project_metadata import (
31+
resource_tracker_project_metadata,
32+
)
2933
from simcore_postgres_database.models.resource_tracker_service_runs import (
3034
resource_tracker_service_runs,
3135
)
@@ -51,6 +55,7 @@ def mock_env(monkeypatch: pytest.MonkeyPatch) -> EnvVarsDict:
5155
"SC_BOOT_MODE": "production",
5256
"POSTGRES_CLIENT_NAME": "postgres_test_client",
5357
"RESOURCE_USAGE_TRACKER_MISSED_HEARTBEAT_CHECK_ENABLED": "0",
58+
"RESOURCE_USAGE_TRACKER_TRACING": "null",
5459
}
5560
setenvs_from_dict(monkeypatch, env_vars)
5661
return env_vars
@@ -181,6 +186,25 @@ async def assert_service_runs_db_row(
181186
raise ValueError
182187

183188

189+
async def assert_project_metadata_db_row(postgres_db, project_id: ProjectID) -> None:
190+
async for attempt in AsyncRetrying(
191+
wait=wait_fixed(0.2),
192+
stop=stop_after_delay(10),
193+
retry=retry_if_exception_type(AssertionError),
194+
reraise=True,
195+
):
196+
with attempt, postgres_db.connect() as con:
197+
result = con.execute(
198+
sa.select(resource_tracker_project_metadata).where(
199+
resource_tracker_project_metadata.c.project_id == f"{project_id}"
200+
)
201+
)
202+
row = result.first()
203+
assert row
204+
return
205+
raise ValueError
206+
207+
184208
async def assert_credit_transactions_db_row(
185209
postgres_db, service_run_id: str, modified_at: datetime | None = None
186210
) -> CreditTransactionDB:
@@ -239,6 +263,7 @@ def _creator(**kwargs: dict[str, Any]) -> RabbitResourceTrackingStartedMessage:
239263
"user_email": faker.email(),
240264
"project_id": faker.uuid4(),
241265
"project_name": faker.pystr(),
266+
"project_tags": [(faker.pyint(), faker.pystr())],
242267
"node_id": faker.uuid4(),
243268
"node_name": faker.pystr(),
244269
"parent_project_id": faker.uuid4(),

services/resource-usage-tracker/tests/unit/with_dbs/test_process_rabbitmq_message.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
_process_stop_event,
1818
)
1919

20-
from .conftest import assert_service_runs_db_row
20+
from .conftest import assert_project_metadata_db_row, assert_service_runs_db_row
2121

2222
pytest_simcore_core_services_selection = ["postgres", "rabbit"]
2323
pytest_simcore_ops_services_selection = [
@@ -48,6 +48,7 @@ async def test_process_event_functions(
4848
)
4949
await _process_start_event(resource_tracker_repo, msg, publisher)
5050
output = await assert_service_runs_db_row(postgres_db, msg.service_run_id)
51+
await assert_project_metadata_db_row(postgres_db, msg.project_id)
5152
assert output.stopped_at is None
5253
assert output.service_run_status == "RUNNING"
5354
first_occurence_of_last_heartbeat_at = output.last_heartbeat_at

0 commit comments

Comments
 (0)