Skip to content

Commit 89ed12a

Browse files
authored
Feat: Support estimated_size_mib in program content schema for cost estimation endpoint (#715)
* Feat: Support estimated_size_mib in program content schema for cost estimation endpoint * fix: lint fixes * fix: lint errors * pyproject version fix * fix: lint fixes * fix: cast estimated_size_mib to Decimal
1 parent 7c9013a commit 89ed12a

File tree

4 files changed

+158
-18
lines changed

4 files changed

+158
-18
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ dependencies = [
4545
"msgpack==1.0.8", # required by aiocache
4646
"multiaddr==0.0.9", # for libp2p-stubs
4747
"orjson>=3.7.7", # Minimum version for Python 3.11
48-
"psycopg2-binary==2.9.10", # Note: psycopg3 is not yet supported by SQLAlchemy
48+
"psycopg2-binary==2.9.10", # Note: psycopg3 is not yet supported by SQLAlchemy
4949
"pycryptodome==3.22.0", # for libp2p-stubs
5050
"pymultihash==0.8.2", # for libp2p-stubs
5151
"pynacl==1.5",

src/aleph/schemas/cost_estimation_messages.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
ProgramContent,
88
StoreContent,
99
)
10+
from aleph_message.models.execution.program import (
11+
CodeContent,
12+
DataContent,
13+
FunctionRuntime,
14+
)
1015
from aleph_message.models.execution.volume import (
1116
EphemeralVolume,
1217
ImmutableVolume,
@@ -37,7 +42,26 @@ class CostEstimationInstanceContent(InstanceContent):
3742
)
3843

3944

45+
class CostEstimationCodeContent(CodeContent):
46+
estimated_size_mib: Optional[int] = None
47+
48+
49+
class CostEstimationFunctionRuntime(FunctionRuntime):
50+
estimated_size_mib: Optional[int] = None
51+
52+
53+
class CostEstimationDataContent(DataContent):
54+
estimated_size_mib: Optional[int] = None
55+
56+
4057
class CostEstimationProgramContent(ProgramContent):
58+
code: CostEstimationCodeContent = Field(description="Code to execute")
59+
runtime: CostEstimationFunctionRuntime = Field(
60+
description="Execution runtime (rootfs with Python interpreter)"
61+
)
62+
data: Optional[CostEstimationDataContent] = Field(
63+
default=None, description="Data to use during computation"
64+
)
4165
volumes: List[CostEstimationMachineVolume] = Field(
4266
default=[], description="Volumes to mount on the filesystem"
4367
)

src/aleph/services/cost.py

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -299,28 +299,67 @@ def _get_execution_volumes_costs(
299299
)
300300

301301
elif isinstance(content, ProgramContent):
302-
volumes += [
303-
RefVolume(
304-
CostType.EXECUTION_PROGRAM_VOLUME_CODE,
305-
content.code.ref,
306-
content.code.use_latest,
307-
),
308-
RefVolume(
309-
CostType.EXECUTION_PROGRAM_VOLUME_RUNTIME,
310-
content.runtime.ref,
311-
content.runtime.use_latest,
312-
),
313-
]
302+
if (
303+
isinstance(content, CostEstimationProgramContent)
304+
and content.code.estimated_size_mib
305+
):
306+
volumes.append(
307+
SizedVolume(
308+
CostType.EXECUTION_PROGRAM_VOLUME_CODE,
309+
Decimal(content.code.estimated_size_mib),
310+
content.code.ref,
311+
)
312+
)
313+
else:
314+
volumes.append(
315+
RefVolume(
316+
CostType.EXECUTION_PROGRAM_VOLUME_CODE,
317+
content.code.ref,
318+
content.code.use_latest,
319+
)
320+
)
314321

315-
if content.data:
322+
if (
323+
isinstance(content, CostEstimationProgramContent)
324+
and content.runtime.estimated_size_mib
325+
):
326+
volumes.append(
327+
SizedVolume(
328+
CostType.EXECUTION_PROGRAM_VOLUME_RUNTIME,
329+
Decimal(content.runtime.estimated_size_mib),
330+
content.runtime.ref,
331+
)
332+
)
333+
else:
316334
volumes.append(
317335
RefVolume(
318-
CostType.EXECUTION_PROGRAM_VOLUME_DATA,
319-
content.data.ref,
320-
content.data.use_latest,
336+
CostType.EXECUTION_PROGRAM_VOLUME_RUNTIME,
337+
content.runtime.ref,
338+
content.runtime.use_latest,
321339
),
322340
)
323341

342+
if content.data:
343+
if (
344+
isinstance(content, CostEstimationProgramContent)
345+
and content.data.estimated_size_mib
346+
):
347+
volumes.append(
348+
SizedVolume(
349+
CostType.EXECUTION_PROGRAM_VOLUME_DATA,
350+
Decimal(content.data.estimated_size_mib),
351+
content.data.ref,
352+
)
353+
)
354+
else:
355+
volumes.append(
356+
RefVolume(
357+
CostType.EXECUTION_PROGRAM_VOLUME_DATA,
358+
content.data.ref,
359+
content.data.use_latest,
360+
),
361+
)
362+
324363
for i, volume in enumerate(content.volumes):
325364
# NOTE: There are legacy volumes with no "mount" property set
326365
# or with same values for different volumes causing unique key constraint errors

tests/services/test_cost_service.py

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from aleph_message.models import ExecutableContent, InstanceContent, PaymentType
66

77
from aleph.db.models import AggregateDb
8+
from aleph.schemas.cost_estimation_messages import CostEstimationProgramContent
89
from aleph.services.cost import (
910
_get_additional_storage_price,
1011
_get_price_aggregate,
@@ -212,6 +213,63 @@ def fixture_flow_instance_message_complete() -> ExecutableContent:
212213
return InstanceContent.parse_obj(content)
213214

214215

216+
@pytest.fixture
217+
def fixture_hold_program_message_complete() -> ExecutableContent:
218+
content = {
219+
"on": {"http": True, "persistent": False},
220+
"code": {
221+
"ref": "cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe",
222+
"encoding": "zip",
223+
"entrypoint": "main:app",
224+
"use_latest": True,
225+
"estimated_size_mib": 2048,
226+
},
227+
"time": 1740986893.735,
228+
"type": "vm-function",
229+
"address": "0xAD8ac12Ae5bC9f6D902cBDd2f0Dd70F43e522BC2",
230+
"payment": {"type": "hold", "chain": "ETH"},
231+
"runtime": {
232+
"ref": "cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe",
233+
"comment": "Aleph Alpine Linux with Python 3.8",
234+
"use_latest": True,
235+
"estimated_size_mib": 1024,
236+
},
237+
"data": {
238+
"ref": "cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe",
239+
"mount": "/data",
240+
"encoding": "zip",
241+
"use_latest": True,
242+
"estimated_size_mib": 2048,
243+
},
244+
"volumes": [
245+
{
246+
"mount": "/mount1",
247+
"ref": "cafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe",
248+
"use_latest": True,
249+
"estimated_size_mib": 512,
250+
},
251+
{
252+
"mount": "/mount2",
253+
"persistence": "host",
254+
"name": "pers1",
255+
"size_mib": 1024,
256+
},
257+
],
258+
"metadata": {"name": "My program", "description": "My program description"},
259+
"resources": {"vcpus": 1, "memory": 128, "seconds": 30},
260+
"variables": {},
261+
"allow_amend": False,
262+
"environment": {
263+
"internet": True,
264+
"aleph_api": True,
265+
"reproducible": False,
266+
"shared_cache": False,
267+
},
268+
}
269+
270+
return CostEstimationProgramContent.parse_obj(content)
271+
272+
215273
def test_compute_cost(
216274
session_factory: DbSessionFactory,
217275
fixture_product_prices_aggregate_in_db,
@@ -290,7 +348,7 @@ def test_get_additional_storage_price(
290348
assert additional_cost == 0
291349

292350

293-
def test_compute_cost_complete(
351+
def test_compute_cost_instance_complete(
294352
session_factory: DbSessionFactory,
295353
fixture_product_prices_aggregate_in_db,
296354
fixture_settings_aggregate_in_db,
@@ -309,6 +367,25 @@ def test_compute_cost_complete(
309367
assert cost == 1017.50
310368

311369

370+
def test_compute_cost_program_complete(
371+
session_factory: DbSessionFactory,
372+
fixture_product_prices_aggregate_in_db,
373+
fixture_settings_aggregate_in_db,
374+
fixture_hold_program_message_complete,
375+
):
376+
file_db = StoredFileDb()
377+
mock = Mock()
378+
mock.patch("_get_file_from_ref", return_value=file_db)
379+
380+
with session_factory() as session:
381+
cost, _ = get_total_and_detailed_costs(
382+
session=session,
383+
content=fixture_hold_program_message_complete,
384+
item_hash="asdf",
385+
)
386+
assert cost == Decimal("630.400000000000000000")
387+
388+
312389
def test_compute_flow_cost(
313390
session_factory: DbSessionFactory,
314391
fixture_product_prices_aggregate_in_db,

0 commit comments

Comments
 (0)