Skip to content

Commit c0608bf

Browse files
authored
Merge pull request #685 from FlorentinD/fix-delete-paused-db
Fix deleting session by name against paused instances
2 parents 9a05f35 + 0273a68 commit c0608bf

File tree

4 files changed

+83
-38
lines changed

4 files changed

+83
-38
lines changed

doc/modules/ROOT/pages/tutorials/gds-sessions.adoc

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,12 @@ data_query = """
124124
(brie:Person {name: 'Brie', age: 31, experience: 6, hipster: 0}),
125125
(elsa:Person {name: 'Elsa', age: 65, experience: 23, hipster: 1}),
126126
(john:Person {name: 'John', age: 4, experience: 100, hipster: 0}),
127-
127+
128128
(apple:Fruit {name: 'Apple', tropical: 0, sourness: 0.3, sweetness: 0.6}),
129129
(banana:Fruit {name: 'Banana', tropical: 1, sourness: 0.1, sweetness: 0.9}),
130130
(mango:Fruit {name: 'Mango', tropical: 1, sourness: 0.3, sweetness: 1.0}),
131131
(plum:Fruit {name: 'Plum', tropical: 0, sourness: 0.5, sweetness: 0.8})
132-
132+
133133
CREATE
134134
(dan)-[:LIKES]->(apple),
135135
(annie)-[:LIKES]->(banana),
@@ -138,7 +138,7 @@ data_query = """
138138
(brie)-[:LIKES]->(banana),
139139
(elsa)-[:LIKES]->(plum),
140140
(john)-[:LIKES]->(plum),
141-
141+
142142
(dan)-[:KNOWS]->(annie),
143143
(dan)-[:KNOWS]->(matt),
144144
(annie)-[:KNOWS]->(matt),
@@ -179,16 +179,16 @@ G, result = gds.graph.project(
179179
CALL {
180180
MATCH (p1:Person)
181181
OPTIONAL MATCH (p1)-[r:KNOWS]->(p2:Person)
182-
RETURN
183-
p1 AS source, r AS rel, p2 AS target,
184-
p1 {.age, .experience, .hipster } AS sourceNodeProperties,
182+
RETURN
183+
p1 AS source, r AS rel, p2 AS target,
184+
p1 {.age, .experience, .hipster } AS sourceNodeProperties,
185185
p2 {.age, .experience, .hipster } AS targetNodeProperties
186186
UNION
187187
MATCH (f:Fruit)
188188
OPTIONAL MATCH (f)<-[r:LIKES]-(p:Person)
189-
RETURN
190-
p AS source, r AS rel, f AS target,
191-
p {.age, .experience, .hipster } AS sourceNodeProperties,
189+
RETURN
190+
p AS source, r AS rel, f AS target,
191+
p {.age, .experience, .hipster } AS sourceNodeProperties,
192192
f { .tropical, .sourness, .sweetness } AS targetNodeProperties
193193
}
194194
RETURN gds.graph.project.remote(source, target, {
@@ -265,8 +265,8 @@ instance.
265265
----
266266
gds.run_cypher(
267267
"""
268-
MATCH (p:Person)
269-
RETURN p.name, p.pagerank AS rank, p.louvain
268+
MATCH (p:Person)
269+
RETURN p.name, p.pagerank AS rank, p.louvain
270270
ORDER BY rank DESC
271271
"""
272272
)
@@ -284,7 +284,9 @@ stop incurring costs.
284284

285285
[source, python, role=no-test]
286286
----
287-
sessions.delete("people-and-fruits")
287+
gds.delete()
288+
289+
# or sessions.delete("people-and-fruits")
288290
----
289291

290292
[source, python, role=no-test]

examples/gds-sessions.ipynb

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,12 @@
181181
" (brie:Person {name: 'Brie', age: 31, experience: 6, hipster: 0}),\n",
182182
" (elsa:Person {name: 'Elsa', age: 65, experience: 23, hipster: 1}),\n",
183183
" (john:Person {name: 'John', age: 4, experience: 100, hipster: 0}),\n",
184-
" \n",
184+
"\n",
185185
" (apple:Fruit {name: 'Apple', tropical: 0, sourness: 0.3, sweetness: 0.6}),\n",
186186
" (banana:Fruit {name: 'Banana', tropical: 1, sourness: 0.1, sweetness: 0.9}),\n",
187187
" (mango:Fruit {name: 'Mango', tropical: 1, sourness: 0.3, sweetness: 1.0}),\n",
188188
" (plum:Fruit {name: 'Plum', tropical: 0, sourness: 0.5, sweetness: 0.8})\n",
189-
" \n",
189+
"\n",
190190
" CREATE\n",
191191
" (dan)-[:LIKES]->(apple),\n",
192192
" (annie)-[:LIKES]->(banana),\n",
@@ -195,7 +195,7 @@
195195
" (brie)-[:LIKES]->(banana),\n",
196196
" (elsa)-[:LIKES]->(plum),\n",
197197
" (john)-[:LIKES]->(plum),\n",
198-
" \n",
198+
"\n",
199199
" (dan)-[:KNOWS]->(annie),\n",
200200
" (dan)-[:KNOWS]->(matt),\n",
201201
" (annie)-[:KNOWS]->(matt),\n",
@@ -242,16 +242,16 @@
242242
" CALL {\n",
243243
" MATCH (p1:Person)\n",
244244
" OPTIONAL MATCH (p1)-[r:KNOWS]->(p2:Person)\n",
245-
" RETURN \n",
246-
" p1 AS source, r AS rel, p2 AS target, \n",
247-
" p1 {.age, .experience, .hipster } AS sourceNodeProperties, \n",
245+
" RETURN\n",
246+
" p1 AS source, r AS rel, p2 AS target,\n",
247+
" p1 {.age, .experience, .hipster } AS sourceNodeProperties,\n",
248248
" p2 {.age, .experience, .hipster } AS targetNodeProperties\n",
249249
" UNION\n",
250250
" MATCH (f:Fruit)\n",
251251
" OPTIONAL MATCH (f)<-[r:LIKES]-(p:Person)\n",
252-
" RETURN \n",
253-
" p AS source, r AS rel, f AS target, \n",
254-
" p {.age, .experience, .hipster } AS sourceNodeProperties, \n",
252+
" RETURN\n",
253+
" p AS source, r AS rel, f AS target,\n",
254+
" p {.age, .experience, .hipster } AS sourceNodeProperties,\n",
255255
" f { .tropical, .sourness, .sweetness } AS targetNodeProperties\n",
256256
" }\n",
257257
" RETURN gds.graph.project.remote(source, target, {\n",
@@ -361,8 +361,8 @@
361361
"source": [
362362
"gds.run_cypher(\n",
363363
" \"\"\"\n",
364-
" MATCH (p:Person) \n",
365-
" RETURN p.name, p.pagerank AS rank, p.louvain \n",
364+
" MATCH (p:Person)\n",
365+
" RETURN p.name, p.pagerank AS rank, p.louvain\n",
366366
" ORDER BY rank DESC\n",
367367
" \"\"\"\n",
368368
")"
@@ -391,7 +391,9 @@
391391
},
392392
"outputs": [],
393393
"source": [
394-
"sessions.delete(\"people-and-fruits\")"
394+
"gds.delete()\n",
395+
"\n",
396+
"# or sessions.delete(\"people-and-fruits\")"
395397
]
396398
},
397399
{

graphdatascience/session/dedicated_sessions.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ def get_or_create(
5151
# hashing the password to avoid storing the actual db password in Aura
5252
password = hashlib.sha256(db_connection.password.encode()).hexdigest()
5353

54-
# TODO configure session size (and check existing_session has same size)
5554
if existing_session:
5655
self._check_expiry_date(existing_session)
5756
self._check_memory_configuration(existing_session, memory.value)
@@ -71,7 +70,7 @@ def get_or_create(
7170
)
7271

7372
return self._construct_client(
74-
session_name=session_name, session_connection=session_connection, db_connection=db_connection
73+
session_id=session_id, session_connection=session_connection, db_connection=db_connection
7574
)
7675

7776
def delete(self, session_name: str, dbid: Optional[str] = None) -> bool:
@@ -106,7 +105,13 @@ def list(self) -> List[SessionInfo]:
106105
return [SessionInfo.from_session_details(i) for i in sessions]
107106

108107
def _find_existing_session(self, session_name: str, dbid: str) -> Optional[SessionDetails]:
109-
matched_sessions = [s for s in self._aura_api.list_sessions(dbid) if s.name == session_name]
108+
matched_sessions: List[SessionDetails] = []
109+
try:
110+
matched_sessions = [s for s in self._aura_api.list_sessions(dbid) if s.name == session_name]
111+
except HTTPError as e:
112+
# ignore 404 errors when listing sessions as it could mean paused sessions or deleted sessions
113+
if e.response.status_code != 404:
114+
raise e
110115

111116
if len(matched_sessions) == 0:
112117
return None
@@ -132,12 +137,14 @@ def _create_session(
132137
return create_details
133138

134139
def _construct_client(
135-
self, session_name: str, session_connection: DbmsConnectionInfo, db_connection: DbmsConnectionInfo
140+
self, session_id: str, session_connection: DbmsConnectionInfo, db_connection: DbmsConnectionInfo
136141
) -> AuraGraphDataScience:
137142
return AuraGraphDataScience(
138143
gds_session_connection_info=session_connection,
139144
aura_db_connection_info=db_connection,
140-
delete_fn=lambda: self.delete(session_name, dbid=AuraApi.extract_id(db_connection.uri)),
145+
delete_fn=lambda: self._aura_api.delete_session(
146+
session_id=session_id, dbid=AuraApi.extract_id(db_connection.uri)
147+
),
141148
)
142149

143150
def _check_expiry_date(self, session: SessionDetails) -> None:

graphdatascience/tests/unit/test_dedicated_sessions.py

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ def add_session(self, session: SessionDetails) -> None:
6969

7070
self._sessions[session.id] = session
7171

72+
# aura behaviour of paused instances not being in an orchestra
73+
def _mimic_paused_db_behaviour(self, dbid: str) -> None:
74+
db = self.list_instance(dbid)
75+
if db and db.status == "paused":
76+
response = Response()
77+
response.status_code = 404
78+
response._content = b"database not found"
79+
raise HTTPError(request=None, response=response)
80+
7281
def create_instance(
7382
self, name: str, memory: SessionMemoryValue, cloud_provider: str, region: str
7483
) -> InstanceCreateDetails:
@@ -104,20 +113,16 @@ def delete_instance(self, instance_id: str) -> InstanceSpecificDetails:
104113
return self._instances.pop(instance_id)
105114

106115
def list_sessions(self, dbid: str) -> List[SessionDetails]:
107-
# mimic aura behaviour of paused instances not being in an orchestra
108-
db = self.list_instance(dbid)
109-
if db and db.status == "paused":
110-
response = Response()
111-
response.status_code = 404
112-
response._content = b"database not found"
113-
raise HTTPError(request=None, response=response)
116+
self._mimic_paused_db_behaviour(dbid)
114117

115118
return [v for _, v in self._sessions.items() if v.instance_id == dbid]
116119

117120
def list_instances(self) -> List[InstanceDetails]:
118121
return [v for _, v in self._instances.items()]
119122

120123
def list_session(self, session_id: str, dbid: str) -> Optional[SessionDetails]:
124+
self._mimic_paused_db_behaviour(dbid)
125+
121126
matched_instance = self._sessions.get(session_id, None)
122127

123128
if matched_instance:
@@ -239,7 +244,7 @@ def test_create_session(mocker: MockerFixture, aura_api: AuraApi) -> None:
239244
"session_connection": DbmsConnectionInfo(
240245
uri="neo4j+s://foo.bar", username="neo4j", password=HASHED_DB_PASSWORD
241246
),
242-
"session_name": "my-session",
247+
"session_id": "ffff0-ffff1",
243248
}
244249
assert [i.name for i in sessions.list()] == ["my-session"]
245250

@@ -269,7 +274,7 @@ def test_get_or_create(mocker: MockerFixture, aura_api: AuraApi) -> None:
269274
"session_connection": DbmsConnectionInfo(
270275
uri="neo4j+s://foo.bar", username="neo4j", password=HASHED_DB_PASSWORD
271276
),
272-
"session_name": "my-session",
277+
"session_id": "ffff0-ffff1",
273278
}
274279
assert gds_args1 == gds_args2
275280

@@ -399,6 +404,35 @@ def test_delete_nonunique_session(aura_api: AuraApi) -> None:
399404
assert len(sessions.list()) == 2
400405

401406

407+
def test_delete_session_paused_instance(aura_api: AuraApi) -> None:
408+
fake_aura_api = cast(FakeAuraApi, aura_api)
409+
410+
fake_aura_api.id_counter += 1
411+
paused_db = InstanceSpecificDetails(
412+
id="4242",
413+
status="paused",
414+
connection_url="foo.bar",
415+
memory=SessionMemory.m_16GB.value,
416+
type="",
417+
region="dresden",
418+
name="paused-db",
419+
tenant_id=fake_aura_api._tenant_id,
420+
cloud_provider="aws",
421+
)
422+
fake_aura_api._instances[paused_db.id] = paused_db
423+
424+
session = aura_api.create_session(
425+
name="gds-session-my-session-name",
426+
dbid=paused_db.id,
427+
pwd="some_pwd",
428+
memory=SessionMemory.m_8GB.value,
429+
)
430+
sessions = DedicatedSessions(aura_api)
431+
432+
# cannot delete session running against a paused instance
433+
assert not sessions.delete(session.name)
434+
435+
402436
def test_create_waiting_forever() -> None:
403437
aura_api = FakeAuraApi(status_after_creating="updating")
404438
_setup_db_instance(aura_api)

0 commit comments

Comments
 (0)