Skip to content

Commit eb66bfc

Browse files
committed
Add new LightweightDebugger implementation
This commit adds a new implmentation of the Cascades Debugger interface, which is lightweight as it only keeps track of statstics around debugger events and doesn't support keeping track of symbols and their names.
1 parent ab7f3c3 commit eb66bfc

File tree

6 files changed

+241
-0
lines changed

6 files changed

+241
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* LightweightDebugger.java
3+
*
4+
* This source file is part of the FoundationDB open source project
5+
*
6+
* Copyright 2015-2025 Apple Inc. and the FoundationDB project authors
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
package com.apple.foundationdb.record.query.plan.cascades.debug;
22+
23+
import com.apple.foundationdb.record.logging.KeyValueLogMessage;
24+
import com.apple.foundationdb.record.query.plan.cascades.PlanContext;
25+
import com.apple.foundationdb.record.query.plan.cascades.Reference;
26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
28+
29+
import javax.annotation.Nonnull;
30+
import javax.annotation.Nullable;
31+
import java.util.Objects;
32+
import java.util.Optional;
33+
import java.util.concurrent.TimeUnit;
34+
35+
/**
36+
* Implementation of a lightweight debugger that only maintains debugging events for calculating {@link Stats}
37+
* (i.e. it implements only the {@link StatsDebugger} interface).
38+
* This debugger is safe for use in production.
39+
*/
40+
public class LightweightDebugger implements StatsDebugger {
41+
private static final Logger logger = LoggerFactory.getLogger(LightweightDebugger.class);
42+
43+
@Nullable
44+
private EventState currentEventState;
45+
@Nullable
46+
private String queryAsString;
47+
@Nullable
48+
private PlanContext planContext;
49+
50+
public LightweightDebugger() {
51+
this.currentEventState = null;
52+
this.planContext = null;
53+
}
54+
55+
@Nonnull
56+
EventState getCurrentEventState() {
57+
return Objects.requireNonNull(currentEventState);
58+
}
59+
60+
@Nullable
61+
@Override
62+
public PlanContext getPlanContext() {
63+
return planContext;
64+
}
65+
66+
@Override
67+
public boolean isSane() {
68+
return true;
69+
}
70+
71+
@Override
72+
public void onInstall() {
73+
// do nothing
74+
}
75+
76+
@Override
77+
public void onSetup() {
78+
reset();
79+
}
80+
81+
@Override
82+
public void onShow(@Nonnull final Reference ref) {
83+
// do nothing
84+
}
85+
86+
@Override
87+
public void onQuery(@Nonnull final String recordQuery, @Nonnull final PlanContext planContext) {
88+
this.queryAsString = recordQuery;
89+
this.planContext = planContext;
90+
91+
logQuery();
92+
}
93+
94+
@Override
95+
public void onEvent(final Debugger.Event event) {
96+
if ((queryAsString == null) || (planContext == null) || (currentEventState == null)) {
97+
return;
98+
}
99+
getCurrentEventState().addCurrentEvent(event);
100+
}
101+
102+
@Override
103+
public void onDone() {
104+
if (currentEventState != null && queryAsString != null) {
105+
final var state = Objects.requireNonNull(currentEventState);
106+
if (logger.isDebugEnabled()) {
107+
logger.debug(KeyValueLogMessage.of("planning done",
108+
"query", Objects.requireNonNull(queryAsString).substring(0, Math.min(queryAsString.length(), 30)),
109+
"duration-in-ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - state.getStartTs()),
110+
"ticks", state.getCurrentTick()));
111+
}
112+
}
113+
reset();
114+
}
115+
116+
@Nonnull
117+
@Override
118+
public Optional<StatsMaps> getStatsMaps() {
119+
if (currentEventState != null) {
120+
return Optional.of(currentEventState.getStatsMaps());
121+
}
122+
return Optional.empty();
123+
}
124+
125+
private void reset() {
126+
this.currentEventState = EventState.initial(false, false, null);
127+
this.planContext = null;
128+
this.queryAsString = null;
129+
}
130+
131+
void logQuery() {
132+
if (logger.isDebugEnabled()) {
133+
logger.debug(KeyValueLogMessage.of("planning started", "query", queryAsString));
134+
}
135+
}
136+
}

yaml-tests/src/main/java/com/apple/foundationdb/relational/yamltests/command/DebuggerImplementation.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import com.apple.foundationdb.record.query.plan.cascades.debug.Debugger;
2424
import com.apple.foundationdb.record.query.plan.cascades.debug.DebuggerWithSymbolTables;
25+
import com.apple.foundationdb.record.query.plan.cascades.debug.LightweightDebugger;
2526
import com.apple.foundationdb.record.query.plan.cascades.debug.PlannerRepl;
2627
import com.apple.foundationdb.relational.yamltests.YamlExecutionContext;
2728
import org.jline.terminal.TerminalBuilder;
@@ -31,6 +32,7 @@
3132
import java.util.function.Function;
3233

3334
public enum DebuggerImplementation {
35+
LIGHTWEIGHT(context -> new LightweightDebugger()),
3436
INSANE(context -> DebuggerWithSymbolTables.withSanityChecks()),
3537
SANE(context -> DebuggerWithSymbolTables.withoutSanityChecks()),
3638
REPL(context -> {

yaml-tests/src/test/java/YamlIntegrationTests.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,4 +305,9 @@ public void literalExtractionTests(YamlTest.Runner runner) throws Exception {
305305
public void caseSensitivityTest(YamlTest.Runner runner) throws Exception {
306306
runner.runYamsql("case-sensitivity.yamsql");
307307
}
308+
309+
@TestTemplate
310+
public void simpleQueryWithDifferentDebuggersTest(YamlTest.Runner runner) throws Exception {
311+
runner.runYamsql("simple-query-with-different-debuggers.yamsql");
312+
}
308313
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
N
3+
%simple-query-with-different-debuggers%EXPLAIN select * from t1 where id > 1�
4+
����"* ���(0��I8@9SCAN(<,>) | FILTER _.ID GREATER_THAN promote(@c8 AS LONG)�
5+
digraph G {
6+
fontname=courier;
7+
rankdir=BT;
8+
splines=polyline;
9+
1 [ label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="8"><tr><td align="left">Predicate Filter</td></tr><tr><td align="left">WHERE q9bd5c851_f08a_4d97_bc5d_1ae41728b1b7.ID GREATER_THAN promote(@c8 AS LONG)</td></tr></table>> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, )" ];
10+
2 [ label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="8"><tr><td align="left">Scan</td></tr><tr><td align="left">range: &lt;-∞, ∞&gt;</td></tr></table>> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, )" ];
11+
3 [ label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="8"><tr><td align="left">Primary Storage</td></tr><tr><td align="left">record types: [T1]</td></tr></table>> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS ID, )" ];
12+
3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
13+
2 -> 1 [ label=<&nbsp;q9bd5c851_f08a_4d97_bc5d_1ae41728b1b7> label="q9bd5c851_f08a_4d97_bc5d_1ae41728b1b7" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ];
14+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
simple-query-with-different-debuggers:
2+
- query: EXPLAIN select * from t1 where id > 1
3+
explain: SCAN(<,>) | FILTER _.ID GREATER_THAN promote(@c8 AS LONG)
4+
task_count: 163
5+
task_total_time_ms: 72
6+
transform_count: 42
7+
transform_time_ms: 61
8+
transform_yield_count: 14
9+
insert_time_ms: 1
10+
insert_new_count: 14
11+
insert_reused_count: 2
12+
- query: EXPLAIN select * from t1 where id > 1
13+
explain: SCAN(<,>) | FILTER _.ID GREATER_THAN promote(@c8 AS LONG)
14+
task_count: 163
15+
task_total_time_ms: 72
16+
transform_count: 42
17+
transform_time_ms: 61
18+
transform_yield_count: 14
19+
insert_time_ms: 1
20+
insert_new_count: 14
21+
insert_reused_count: 2
22+
- query: EXPLAIN select * from t1 where id > 1
23+
explain: SCAN(<,>) | FILTER _.ID GREATER_THAN promote(@c8 AS LONG)
24+
task_count: 163
25+
task_total_time_ms: 72
26+
transform_count: 42
27+
transform_time_ms: 61
28+
transform_yield_count: 14
29+
insert_time_ms: 1
30+
insert_new_count: 14
31+
insert_reused_count: 2
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#
2+
# simple-query-with-different-debuggers.yamsql
3+
#
4+
# This source file is part of the FoundationDB open source project
5+
#
6+
# Copyright 2021-2025 Apple Inc. and the FoundationDB project authors
7+
#
8+
# Licensed under the Apache License, Version 2.0 (the "License");
9+
# you may not use this file except in compliance with the License.
10+
# You may obtain a copy of the License at
11+
#
12+
# http://www.apache.org/licenses/LICENSE-2.0
13+
#
14+
# Unless required by applicable law or agreed to in writing, software
15+
# distributed under the License is distributed on an "AS IS" BASIS,
16+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
# See the License for the specific language governing permissions and
18+
# limitations under the License.
19+
20+
---
21+
schema_template:
22+
create table t1(id bigint, col1 string, primary key(id))
23+
---
24+
setup:
25+
steps:
26+
- query: INSERT INTO T1 VALUES
27+
(1, 'a'),
28+
(2, 'b'),
29+
(3, 'c')
30+
---
31+
test_block:
32+
name: simple-query-with-different-debuggers
33+
preset: single_repetition_ordered
34+
tests:
35+
-
36+
- query: select * from t1 where id > 1
37+
- explain: "SCAN(<,>) | FILTER _.ID GREATER_THAN promote(@c8 AS LONG)"
38+
- debugger: lightweight
39+
- result: [{id: 2, col1: 'b'},
40+
{id: 3, col1: 'c'}]
41+
-
42+
- query: select * from t1 where id > 1
43+
- explain: "SCAN(<,>) | FILTER _.ID GREATER_THAN promote(@c8 AS LONG)"
44+
- debugger: sane
45+
- result: [{id: 2, col1: 'b'},
46+
{id: 3, col1: 'c'}]
47+
-
48+
- query: select * from t1 where id > 1
49+
- explain: "SCAN(<,>) | FILTER _.ID GREATER_THAN promote(@c8 AS LONG)"
50+
- debugger: insane
51+
- result: [{id: 2, col1: 'b'},
52+
{id: 3, col1: 'c'}]
53+
...

0 commit comments

Comments
 (0)