Skip to content

Commit c226db8

Browse files
Merge pull request #35 from nodestream-proj/RecordMetrics
Neo4j Operations Metrics
2 parents 002c8e8 + 02f35b5 commit c226db8

File tree

5 files changed

+65
-8
lines changed

5 files changed

+65
-8
lines changed

nodestream_plugin_neo4j/neo4j_database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ async def _execute_query(
126126

127127
def log_error_messages_from_statistics(self, statistics: Neo4jQueryStatistics):
128128
for error in statistics.error_messages:
129-
self.logger.error("Query Error Occurred.", extra={"error": error})
129+
self.logger.info("Query Error Occurred.", extra={"error": error})
130130

131131
async def execute(
132132
self,

nodestream_plugin_neo4j/result.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,17 @@
8585
"Number of indexes removed in the Neo4j query.",
8686
accumulate=True,
8787
)
88+
OPERATIONS_COMMITTED = Metric(
89+
"neo4j_query_write_metrics_operations_committed",
90+
"Number of operations committed in the Neo4j query.",
91+
accumulate=True,
92+
)
93+
94+
OPERATIONS_MISSING = Metric(
95+
"neo4j_query_write_metrics_operations_missing",
96+
"Number of operations missing in the Neo4j query.",
97+
accumulate=True,
98+
)
8899

89100
# APOC specific metrics
90101
WAS_TERMINATED = Metric(
@@ -129,6 +140,8 @@ class Neo4jWriteMetrics:
129140
constraints_removed: int = 0
130141
indexes_added: int = 0
131142
indexes_removed: int = 0
143+
operations_committed: int = 0
144+
operations_missing: int = 0
132145

133146

134147
@dataclass
@@ -167,6 +180,14 @@ def from_result(
167180
if apoc_response:
168181
stats.was_terminated = apoc_response.wasTerminated
169182
stats.retries = apoc_response.retries
183+
stats.write_metrics.operations_committed = apoc_response.committedOperations
184+
185+
# Operations are missing if there are no committed operations and only failed operations.
186+
stats.write_metrics.operations_missing = (
187+
apoc_response.failedOperations
188+
if apoc_response.committedOperations == 0
189+
else 0
190+
)
170191

171192
# Set APOC timing if available
172193
if hasattr(apoc_response, "timeTaken"):
@@ -186,6 +207,12 @@ def from_result(
186207
properties_set=apoc_response.updateStatistics.propertiesSet,
187208
labels_added=apoc_response.updateStatistics.labelsAdded,
188209
labels_removed=apoc_response.updateStatistics.labelsRemoved,
210+
operations_committed=apoc_response.committedOperations,
211+
operations_missing=(
212+
apoc_response.failedOperations
213+
if apoc_response.committedOperations == 0
214+
else 0
215+
),
189216
)
190217
else:
191218
# Set write metrics from query summary
@@ -224,6 +251,8 @@ def update_metrics_from_summary(self):
224251
(CONSTRAINTS_REMOVED, self.write_metrics.constraints_removed),
225252
(INDEXES_ADDED, self.write_metrics.indexes_added),
226253
(INDEXES_REMOVED, self.write_metrics.indexes_removed),
254+
(OPERATIONS_COMMITTED, self.write_metrics.operations_committed),
255+
(OPERATIONS_MISSING, self.write_metrics.operations_missing),
227256
(WAS_TERMINATED, int(self.was_terminated)),
228257
(RETRIES, self.retries),
229258
(ERROR_MESSAGES, len(self.error_messages)),

poetry.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
[tool.poetry]
22
name = "nodestream-plugin-neo4j"
3-
version = "0.14.3"
3+
version = "0.14.4"
44
description = ""
55
authors = ["Zach Probst <[email protected]>"]
66
readme = "README.md"
77

88
[tool.poetry.dependencies]
99
python = "^3.10"
10-
nodestream = "^0.14.12"
10+
nodestream = "^0.14.14"
1111
neo4j = "^5.16.0"
1212
cymple = "^0.11.0"
1313
dacite = "^1.9.2"

tests/unit/test_result.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
LABELS_REMOVED,
1515
NODES_CREATED,
1616
NODES_DELETED,
17+
OPERATIONS_COMMITTED,
18+
OPERATIONS_MISSING,
1719
PLANNING_TIME,
1820
PROCESSING_TIME,
1921
PROPERTIES_SET,
@@ -63,6 +65,8 @@ def test_neo4j_write_metrics_default_initialization():
6365
assert_that(metrics.constraints_removed, equal_to(0))
6466
assert_that(metrics.indexes_added, equal_to(0))
6567
assert_that(metrics.indexes_removed, equal_to(0))
68+
assert_that(metrics.operations_committed, equal_to(0))
69+
assert_that(metrics.operations_missing, equal_to(0))
6670

6771

6872
def test_neo4j_write_metrics_custom_initialization():
@@ -78,6 +82,8 @@ def test_neo4j_write_metrics_custom_initialization():
7882
constraints_removed=1,
7983
indexes_added=3,
8084
indexes_removed=1,
85+
operations_committed=7,
86+
operations_missing=1,
8187
)
8288
assert_that(metrics.nodes_created, equal_to(5))
8389
assert_that(metrics.nodes_deleted, equal_to(2))
@@ -90,6 +96,8 @@ def test_neo4j_write_metrics_custom_initialization():
9096
assert_that(metrics.constraints_removed, equal_to(1))
9197
assert_that(metrics.indexes_added, equal_to(3))
9298
assert_that(metrics.indexes_removed, equal_to(1))
99+
assert_that(metrics.operations_committed, equal_to(7))
100+
assert_that(metrics.operations_missing, equal_to(1))
93101

94102

95103
def test_neo4j_query_statistics_default_initialization():
@@ -162,6 +170,10 @@ def test_neo4j_query_statistics_from_result_without_apoc():
162170
assert_that(stats.write_metrics.indexes_added, equal_to(3))
163171
assert_that(stats.write_metrics.indexes_removed, equal_to(1))
164172

173+
# Operations committed/missing should remain defaults for non-APOC
174+
assert_that(stats.write_metrics.operations_committed, equal_to(0))
175+
assert_that(stats.write_metrics.operations_missing, equal_to(0))
176+
165177
assert_that(stats.was_terminated, equal_to(False))
166178
assert_that(stats.retries, equal_to(0))
167179
assert_that(stats.error_messages, empty())
@@ -203,6 +215,8 @@ def test_neo4j_query_statistics_from_result_with_apoc():
203215
wasTerminated=True,
204216
retries=2,
205217
errorMessages={"batch1": "error1", "batch2": "error2"},
218+
committedOperations=13,
219+
failedOperations=2,
206220
updateStatistics=apoc_update_stats,
207221
)
208222

@@ -220,6 +234,8 @@ def test_neo4j_query_statistics_from_result_with_apoc():
220234
assert_that(stats.write_metrics.properties_set, equal_to(20))
221235
assert_that(stats.write_metrics.labels_added, equal_to(5))
222236
assert_that(stats.write_metrics.labels_removed, equal_to(2))
237+
assert_that(stats.write_metrics.operations_committed, equal_to(13))
238+
assert_that(stats.write_metrics.operations_missing, equal_to(0))
223239

224240
assert_that(stats.was_terminated, equal_to(True))
225241
assert_that(stats.retries, equal_to(2))
@@ -240,6 +256,8 @@ def test_neo4j_query_statistics_from_result_with_apoc_no_update_stats():
240256
wasTerminated=False,
241257
retries=1,
242258
errorMessages={},
259+
committedOperations=0,
260+
failedOperations=4,
243261
updateStatistics=None,
244262
)
245263

@@ -250,6 +268,10 @@ def test_neo4j_query_statistics_from_result_with_apoc_no_update_stats():
250268
assert_that(stats.timing.apoc_time_ms, equal_to(1500))
251269
assert_that(stats.error_messages, empty())
252270

271+
# Operations committed/missing inferred from APOC response
272+
assert_that(stats.write_metrics.operations_committed, equal_to(0))
273+
assert_that(stats.write_metrics.operations_missing, equal_to(4))
274+
253275
# Write metrics should be defaults since no update statistics
254276
assert_that(stats.write_metrics.nodes_created, equal_to(0))
255277
assert_that(stats.write_metrics.relationships_created, equal_to(0))
@@ -367,6 +389,8 @@ def test_update_metrics_from_summary(mocker):
367389
constraints_removed=1,
368390
indexes_added=3,
369391
indexes_removed=1,
392+
operations_committed=7,
393+
operations_missing=1,
370394
)
371395

372396
stats = Neo4jQueryStatistics(
@@ -396,13 +420,15 @@ def test_update_metrics_from_summary(mocker):
396420
mocker.call(CONSTRAINTS_REMOVED, 1),
397421
mocker.call(INDEXES_ADDED, 3),
398422
mocker.call(INDEXES_REMOVED, 1),
423+
mocker.call(OPERATIONS_COMMITTED, 7),
424+
mocker.call(OPERATIONS_MISSING, 1),
399425
mocker.call(WAS_TERMINATED, 1), # True converted to int
400426
mocker.call(RETRIES, 2),
401427
mocker.call(ERROR_MESSAGES, 2), # len(error_messages)
402428
]
403429

404430
mock_metrics.increment.assert_has_calls(expected_calls, any_order=True)
405-
assert_that(mock_metrics.increment.call_count, equal_to(18))
431+
assert_that(mock_metrics.increment.call_count, equal_to(20))
406432

407433

408434
def test_metric_constants_are_defined():
@@ -423,6 +449,8 @@ def test_metric_constants_are_defined():
423449
CONSTRAINTS_REMOVED,
424450
INDEXES_ADDED,
425451
INDEXES_REMOVED,
452+
OPERATIONS_COMMITTED,
453+
OPERATIONS_MISSING,
426454
WAS_TERMINATED,
427455
RETRIES,
428456
ERROR_MESSAGES,

0 commit comments

Comments
 (0)