Skip to content

Commit aa2898c

Browse files
committed
Reapply "Use new estimate endpoint on Aura API"
This reverts commit 5f1fbf1.
1 parent 57f2917 commit aa2898c

File tree

8 files changed

+96
-18
lines changed

8 files changed

+96
-18
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- Added retries to verifying connection and authentication of DB connection provided to `GdsSessions.getOrCreate`
1717
- AuraAPI `delete_instance` will no longer fail when receiving a code 410 (Already Exists)
1818
- It is no longer necessary to specify TLS certificates for Arrow when running on Windows
19+
- `GdsSessions.estimate` now recommends smaller sizes such as `2GB`. Also allows specifying property and label counts for better estimates.
1920

2021
## Other changes
2122

doc/modules/ROOT/pages/graph-analytics-serverless.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ memory = sessions.estimate(
213213
node_count=20,
214214
relationship_count=50,
215215
algorithm_categories=[AlgorithmCategory.CENTRALITY, AlgorithmCategory.NODE_EMBEDDING],
216+
node_label_count=1,
217+
node_property_count=1,
218+
relationship_property_count=1
216219
)
217220
----
218221

graphdatascience/session/aura_api.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,13 +305,21 @@ def wait_for_instance_running(
305305
return WaitResult.from_error(f"Instance is not running after waiting for {waited_time} seconds")
306306

307307
def estimate_size(
308-
self, node_count: int, relationship_count: int, algorithm_categories: list[AlgorithmCategory]
308+
self,
309+
node_count: int,
310+
node_label_count: int,
311+
node_property_count: int,
312+
relationship_count: int,
313+
relationship_property_count: int,
314+
algorithm_categories: list[AlgorithmCategory],
309315
) -> EstimationDetails:
310316
data = {
311317
"node_count": node_count,
318+
"node_label_count": node_label_count,
319+
"node_property_count": node_property_count,
312320
"relationship_count": relationship_count,
321+
"relationship_property_count": relationship_property_count,
313322
"algorithm_categories": [i.value for i in algorithm_categories],
314-
"instance_type": "dsenterprise",
315323
}
316324

317325
response = self._request_session.post(

graphdatascience/session/aura_api_responses.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,8 @@ def from_json(cls, json: dict[str, Any]) -> InstanceCreateDetails:
169169

170170
@dataclass(repr=True, frozen=True)
171171
class EstimationDetails:
172-
min_required_memory: str
172+
estimated_memory: str
173173
recommended_size: str
174-
did_exceed_maximum: bool
175174

176175
@classmethod
177176
def from_json(cls, json: dict[str, Any]) -> EstimationDetails:
@@ -181,6 +180,26 @@ def from_json(cls, json: dict[str, Any]) -> EstimationDetails:
181180

182181
return cls(**{f.name: json[f.name] for f in fields})
183182

183+
def exceeds_recommended(self) -> bool:
184+
return EstimationDetails._parse_size(self.estimated_memory) > EstimationDetails._parse_size(
185+
self.recommended_size
186+
)
187+
188+
@staticmethod
189+
def _parse_size(size: str) -> float:
190+
size_str = size.upper().strip()
191+
# treat GB, Gi and G the same as its only used for comparing it internally
192+
size_str = size_str.removesuffix("B").removesuffix("I")
193+
194+
if size_str.endswith("G"):
195+
return float(size_str[:-1]) * 1024**3 # 1GB = 1024^3 bytes
196+
elif size_str.endswith("M"):
197+
return float(size_str[:-1]) * 1024**2 # 1MB = 1024^2 bytes
198+
elif size_str.endswith("K"):
199+
return float(size_str[:-1]) * 1024 # 1KB = 1024 bytes
200+
else:
201+
return float(size_str) # assume bytes
202+
184203

185204
class WaitResult(NamedTuple):
186205
connection_url: str

graphdatascience/session/dedicated_sessions.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,24 @@ def estimate(
3030
node_count: int,
3131
relationship_count: int,
3232
algorithm_categories: list[AlgorithmCategory] | None = None,
33+
node_label_count: int = 0,
34+
node_property_count: int = 0,
35+
relationship_property_count: int = 0,
3336
) -> SessionMemory:
3437
if algorithm_categories is None:
3538
algorithm_categories = []
36-
estimation = self._aura_api.estimate_size(node_count, relationship_count, algorithm_categories)
39+
estimation = self._aura_api.estimate_size(
40+
node_count=node_count,
41+
node_label_count=node_label_count,
42+
node_property_count=node_property_count,
43+
relationship_count=relationship_count,
44+
relationship_property_count=relationship_property_count,
45+
algorithm_categories=algorithm_categories,
46+
)
3747

38-
if estimation.did_exceed_maximum:
48+
if estimation.exceeds_recommended():
3949
warnings.warn(
40-
f"The estimated memory `{estimation.min_required_memory}` exceeds the maximum size"
50+
f"The estimated memory `{estimation.estimated_memory}` exceeds the maximum size"
4151
f" supported by your Aura project (`{estimation.recommended_size}`).",
4252
ResourceWarning,
4353
)

graphdatascience/session/gds_sessions.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,34 @@ def estimate(
7171
self,
7272
node_count: int,
7373
relationship_count: int,
74-
algorithm_categories: list[AlgorithmCategory] | None = None,
74+
algorithm_categories: Optional[list[AlgorithmCategory]] = None,
75+
node_label_count: int = 0,
76+
node_property_count: int = 0,
77+
relationship_property_count: int = 0,
7578
) -> SessionMemory:
7679
"""
7780
Estimates the memory required for a session with the given node and relationship counts.
7881
7982
Args:
8083
node_count (int): The number of nodes.
8184
relationship_count (int): The number of relationships.
82-
algorithm_categories (list[AlgorithmCategory] | None): The algorithm categories to consider.
83-
85+
algorithm_categories (Optional[list[AlgorithmCategory]]): The algorithm categories to consider.
86+
node_label_count (int): The number of node labels.
87+
node_property_count (int): The number of node properties.
88+
relationship_property_count (int): The number of relationship properties.
8489
Returns:
8590
SessionMemory: The estimated memory required for the session.
8691
"""
8792
if algorithm_categories is None:
8893
algorithm_categories = []
89-
return self._impl.estimate(node_count, relationship_count, algorithm_categories)
94+
return self._impl.estimate(
95+
node_count=node_count,
96+
relationship_count=relationship_count,
97+
algorithm_categories=algorithm_categories,
98+
node_label_count=node_label_count,
99+
node_property_count=node_property_count,
100+
relationship_property_count=relationship_property_count,
101+
)
90102

91103
def available_cloud_locations(self) -> list[CloudLocation]:
92104
"""

graphdatascience/tests/unit/test_aura_api.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,12 +1099,14 @@ def test_wait_for_instance_deleting(requests_mock: Mocker) -> None:
10991099
def test_estimate_size(requests_mock: Mocker) -> None:
11001100
mock_auth_token(requests_mock)
11011101
requests_mock.post(
1102-
"https://api.neo4j.io/v1/instances/sizing",
1103-
json={"data": {"did_exceed_maximum": True, "min_required_memory": "307GB", "recommended_size": "96GB"}},
1102+
"https://api.neo4j.io/v1/graph-analytics/sessions/sizing",
1103+
json={"data": {"estimated_memory": "3070GB", "recommended_size": "512GB"}},
11041104
)
11051105

11061106
api = AuraApi("", "", project_id="some-tenant")
1107-
assert api.estimate_size(100, 10, [AlgorithmCategory.NODE_EMBEDDING]) == EstimationDetails("307GB", "96GB", True)
1107+
assert api.estimate_size(100, 1, 1, 10, 1, [AlgorithmCategory.NODE_EMBEDDING]) == EstimationDetails(
1108+
estimated_memory="3070GB", recommended_size="512GB"
1109+
)
11081110

11091111

11101112
def test_extract_id() -> None:
@@ -1215,3 +1217,20 @@ def test_parse_session_info_without_optionals() -> None:
12151217
project_id="tenant-1",
12161218
user_id="user-1",
12171219
)
1220+
1221+
1222+
def test_estimate_size_parsing() -> None:
1223+
assert EstimationDetails._parse_size("8GB") == 8589934592
1224+
assert EstimationDetails._parse_size("8G") == 8589934592
1225+
assert EstimationDetails._parse_size("512MB") == 536870912
1226+
assert EstimationDetails._parse_size("256KB") == 262144
1227+
assert EstimationDetails._parse_size("1024B") == 1024
1228+
assert EstimationDetails._parse_size("12345") == 12345
1229+
1230+
1231+
def test_estimate_exceeds_maximum() -> None:
1232+
estimation = EstimationDetails(estimated_memory="16Gi", recommended_size="8Gi")
1233+
assert estimation.exceeds_recommended() is True
1234+
1235+
estimation = EstimationDetails(estimated_memory="8Gi", recommended_size="16Gi")
1236+
assert estimation.exceeds_recommended() is False

graphdatascience/tests/unit/test_dedicated_sessions.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def __init__(
4949
self.id_counter = 0
5050
self.time = 0
5151
self._status_after_creating = status_after_creating
52-
self._size_estimation = size_estimation or EstimationDetails("1GB", "8GB", False)
52+
self._size_estimation = size_estimation or EstimationDetails("1GB", "8GB")
5353
self._console_user = console_user
5454
self._admin_user = admin_user
5555

@@ -222,7 +222,13 @@ def project_details(self) -> ProjectDetails:
222222
return ProjectDetails(id=self._project_id, cloud_locations={CloudLocation("aws", "leipzig-1")})
223223

224224
def estimate_size(
225-
self, node_count: int, relationship_count: int, algorithm_categories: list[AlgorithmCategory]
225+
self,
226+
node_count: int,
227+
node_label_count: int,
228+
node_property_count: int,
229+
relationship_count: int,
230+
relationship_property_count: int,
231+
algorithm_categories: list[AlgorithmCategory],
226232
) -> EstimationDetails:
227233
return self._size_estimation
228234

@@ -767,14 +773,14 @@ def test_create_waiting_forever(
767773

768774

769775
def test_estimate_size() -> None:
770-
aura_api = FakeAuraApi(size_estimation=EstimationDetails("1GB", "8GB", False))
776+
aura_api = FakeAuraApi(size_estimation=EstimationDetails("1GB", "8GB"))
771777
sessions = DedicatedSessions(aura_api)
772778

773779
assert sessions.estimate(1, 1, [AlgorithmCategory.CENTRALITY]) == SessionMemory.m_8GB
774780

775781

776782
def test_estimate_size_exceeds() -> None:
777-
aura_api = FakeAuraApi(size_estimation=EstimationDetails("16GB", "8GB", True))
783+
aura_api = FakeAuraApi(size_estimation=EstimationDetails("16GB", "8GB"))
778784
sessions = DedicatedSessions(aura_api)
779785

780786
with pytest.warns(

0 commit comments

Comments
 (0)