Skip to content

Commit a23baaf

Browse files
authored
Merge pull request steve-community#1429 from steve-community/issue1219
Suboptimal DB views with slow queries steve-community#1219
2 parents e1b9479 + 79e72af commit a23baaf

File tree

4 files changed

+246
-4
lines changed

4 files changed

+246
-4
lines changed

src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
import org.jooq.Record1;
4242
import org.jooq.SelectConditionStep;
4343
import org.jooq.impl.DSL;
44-
import org.springframework.beans.factory.annotation.Autowired;
4544
import org.springframework.stereotype.Repository;
4645
import org.springframework.util.CollectionUtils;
4746

@@ -68,10 +67,11 @@
6867
*/
6968
@Slf4j
7069
@Repository
70+
@RequiredArgsConstructor
7171
public class OcppServerRepositoryImpl implements OcppServerRepository {
7272

73-
@Autowired private DSLContext ctx;
74-
@Autowired private ReservationRepository reservationRepository;
73+
private final DSLContext ctx;
74+
private final ReservationRepository reservationRepository;
7575

7676
private final Striped<Lock> transactionTableLocks = Striped.lock(16);
7777

src/main/java/de/rwth/idsg/steve/repository/impl/OcppTagRepositoryImpl.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.jooq.DSLContext;
3232
import org.jooq.JoinType;
3333
import org.jooq.Record10;
34-
import org.jooq.Record7;
3534
import org.jooq.RecordMapper;
3635
import org.jooq.Result;
3736
import org.jooq.SelectQuery;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
-- came with https://github.com/steve-community/steve/issues/1219
2+
CREATE OR REPLACE VIEW ocpp_tag_activity AS
3+
select `o`.*,
4+
count(`t`.`id_tag`) AS `active_transaction_count`,
5+
case when count(`t`.`id_tag`) > 0 then 1 else 0 end AS `in_transaction`,
6+
case when `o`.`max_active_transaction_count` = 0 then 1 else 0 end AS `blocked`
7+
from `ocpp_tag` `o` left join `transaction` `t` on (
8+
`o`.`id_tag` = `t`.`id_tag` and
9+
`t`.`stop_timestamp` is null and
10+
`t`.`stop_value` is null)
11+
group by
12+
`o`.`ocpp_tag_pk`,
13+
`o`.`parent_id_tag`,
14+
`o`.`expiry_date`,
15+
`o`.`max_active_transaction_count`,
16+
`o`.`note`;
17+
18+
CREATE OR REPLACE VIEW `transaction` AS
19+
SELECT
20+
tx1.transaction_pk,
21+
tx1.connector_pk,
22+
tx1.id_tag,
23+
tx1.event_timestamp as 'start_event_timestamp',
24+
tx1.start_timestamp,
25+
tx1.start_value,
26+
tx2.event_actor as 'stop_event_actor',
27+
tx2.event_timestamp as 'stop_event_timestamp',
28+
tx2.stop_timestamp,
29+
tx2.stop_value,
30+
tx2.stop_reason
31+
FROM transaction_start tx1
32+
LEFT JOIN transaction_stop tx2
33+
ON tx1.transaction_pk = tx2.transaction_pk
34+
AND tx2.event_timestamp = (SELECT MAX(event_timestamp) FROM transaction_stop s2 WHERE tx2.transaction_pk = s2.transaction_pk)
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/*
2+
* SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve
3+
* Copyright (C) 2013-2024 SteVe Community Team
4+
* All Rights Reserved.
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
package de.rwth.idsg.steve.issues;
20+
21+
import de.rwth.idsg.steve.SteveException;
22+
import de.rwth.idsg.steve.repository.dto.InsertTransactionParams;
23+
import de.rwth.idsg.steve.repository.dto.OcppTag;
24+
import de.rwth.idsg.steve.repository.dto.Transaction;
25+
import de.rwth.idsg.steve.repository.dto.UpdateTransactionParams;
26+
import de.rwth.idsg.steve.repository.impl.AddressRepositoryImpl;
27+
import de.rwth.idsg.steve.repository.impl.ChargePointRepositoryImpl;
28+
import de.rwth.idsg.steve.repository.impl.OcppServerRepositoryImpl;
29+
import de.rwth.idsg.steve.repository.impl.OcppTagRepositoryImpl;
30+
import de.rwth.idsg.steve.repository.impl.ReservationRepositoryImpl;
31+
import de.rwth.idsg.steve.repository.impl.TransactionRepositoryImpl;
32+
import de.rwth.idsg.steve.web.dto.OcppTagForm;
33+
import de.rwth.idsg.steve.web.dto.OcppTagQueryForm;
34+
import de.rwth.idsg.steve.web.dto.TransactionQueryForm;
35+
import jooq.steve.db.enums.TransactionStopEventActor;
36+
import lombok.RequiredArgsConstructor;
37+
import org.joda.time.DateTime;
38+
import org.joda.time.Duration;
39+
import org.joda.time.LocalDateTime;
40+
import org.jooq.DSLContext;
41+
import org.jooq.SQLDialect;
42+
import org.jooq.impl.DSL;
43+
import org.jooq.impl.DataSourceConnectionProvider;
44+
import org.jooq.impl.DefaultConfiguration;
45+
import org.jooq.tools.jdbc.SingleConnectionDataSource;
46+
47+
import java.sql.Connection;
48+
import java.sql.DriverManager;
49+
import java.util.ArrayList;
50+
import java.util.List;
51+
import java.util.Objects;
52+
import java.util.UUID;
53+
import java.util.concurrent.ThreadLocalRandom;
54+
import java.util.stream.Collectors;
55+
import java.util.stream.IntStream;
56+
57+
@RequiredArgsConstructor
58+
public class Issue1219 {
59+
60+
private static final String url = "jdbc:mysql://localhost:3306/stevedb";
61+
private static final String userName = "steve";
62+
private static final String password = "changeme";
63+
64+
private final DSLContext ctx;
65+
66+
public static void main(String[] args) throws Exception {
67+
Connection con = DriverManager.getConnection(url, userName, password);
68+
69+
org.jooq.Configuration conf = new DefaultConfiguration()
70+
.set(SQLDialect.MYSQL)
71+
.set(new DataSourceConnectionProvider(new SingleConnectionDataSource(con)));
72+
73+
DSLContext ctx = DSL.using(conf);
74+
75+
Issue1219 issue1219 = new Issue1219(ctx);
76+
77+
var ocppTags = issue1219.insertOcppTags(1_000);
78+
System.out.println("inserted ocppTags");
79+
80+
var chargeBoxIds = issue1219.insertChargeBoxes(500);
81+
System.out.println("inserted chargeBoxIds");
82+
83+
var transactionIds = issue1219.insertStartTransactions(10_000, ocppTags, chargeBoxIds);
84+
System.out.println("inserted transaction_starts");
85+
86+
var stoppedTransactionIds = issue1219.insertStopTransactions(transactionIds);
87+
System.out.println("inserted transaction_stops: " + stoppedTransactionIds.size());
88+
89+
System.out.println("-- TESTING --");
90+
issue1219.realTest();
91+
}
92+
93+
private void realTest() {
94+
var repository = new OcppTagRepositoryImpl(ctx);
95+
96+
long start = System.currentTimeMillis();
97+
List<OcppTag.Overview> values = repository.getOverview(new OcppTagQueryForm());
98+
long stop = System.currentTimeMillis();
99+
100+
System.out.println("took " + Duration.millis(stop - start));
101+
}
102+
103+
private List<Integer> insertStopTransactions(List<Integer> insertedTransactionIds) {
104+
var ocppServerRepository = new OcppServerRepositoryImpl(ctx, new ReservationRepositoryImpl(ctx));
105+
var transactionRepository = new TransactionRepositoryImpl(ctx);
106+
107+
List<Integer> stopped = new ArrayList<>();
108+
for (Integer transactionId : insertedTransactionIds) {
109+
if (!ThreadLocalRandom.current().nextBoolean()) {
110+
continue;
111+
}
112+
113+
TransactionQueryForm form = new TransactionQueryForm();
114+
form.setTransactionPk(transactionId);
115+
Transaction transaction = transactionRepository.getTransactions(form).get(0);
116+
117+
DateTime stopTimestamp = transaction.getStartTimestamp().plusHours(1);
118+
UpdateTransactionParams p = UpdateTransactionParams.builder()
119+
.chargeBoxId(transaction.getChargeBoxId())
120+
.transactionId(transaction.getId())
121+
.stopTimestamp(stopTimestamp)
122+
.eventTimestamp(stopTimestamp)
123+
.stopMeterValue(transaction.getStartValue() + "0")
124+
.eventActor(TransactionStopEventActor.station)
125+
.build();
126+
127+
ocppServerRepository.updateTransaction(p);
128+
System.out.println("stopped transaction " + transactionId);
129+
stopped.add(transactionId);
130+
}
131+
return stopped;
132+
}
133+
134+
private List<Integer> insertStartTransactions(int count, List<String> ocppTags, List<String> chargeBoxIds) {
135+
var repository = new OcppServerRepositoryImpl(ctx, new ReservationRepositoryImpl(ctx));
136+
137+
List<Integer> transactionIds = new ArrayList<>();
138+
for (int i = 0; i < count; i++) {
139+
DateTime now = DateTime.now();
140+
InsertTransactionParams params = InsertTransactionParams.builder()
141+
.idTag(ocppTags.get(ThreadLocalRandom.current().nextInt(0, ocppTags.size())))
142+
.chargeBoxId(chargeBoxIds.get(ThreadLocalRandom.current().nextInt(0, chargeBoxIds.size())))
143+
.connectorId(ThreadLocalRandom.current().nextInt(4))
144+
.startMeterValue(String.valueOf(ThreadLocalRandom.current().nextLong(5_000, 20_000)))
145+
.startTimestamp(now)
146+
.eventTimestamp(now)
147+
.build();
148+
int transactionId = repository.insertTransaction(params);
149+
System.out.println("started transaction " + transactionId);
150+
transactionIds.add(transactionId);
151+
}
152+
return transactionIds;
153+
}
154+
155+
private List<String> insertChargeBoxes(int count) {
156+
var repository = new ChargePointRepositoryImpl(ctx, new AddressRepositoryImpl());
157+
158+
List<String> ids = IntStream.range(0, count).mapToObj(val -> UUID.randomUUID().toString()).collect(Collectors.toList());
159+
repository.addChargePointList(ids);
160+
161+
return ids;
162+
}
163+
164+
private List<String> insertOcppTags(int count) {
165+
var repository = new OcppTagRepositoryImpl(ctx);
166+
167+
List<String> idTags = IntStream.range(0, count).mapToObj(val -> UUID.randomUUID().toString()).collect(Collectors.toList());
168+
List<String> insertedTags = new ArrayList<>();
169+
170+
for (String idTag : idTags) {
171+
OcppTagForm form = new OcppTagForm();
172+
form.setIdTag(idTag);
173+
form.setExpiryDate(getRandomExpiry());
174+
form.setParentIdTag(getRandomParentIdTag(idTag, insertedTags));
175+
form.setMaxActiveTransactionCount(ThreadLocalRandom.current().nextInt(1, 4));
176+
177+
try {
178+
repository.addOcppTag(form);
179+
} catch (SteveException.AlreadyExists e) {
180+
// because the referenced idTag was not inserted yet. just inserted without it.
181+
form.setParentIdTag(null);
182+
repository.addOcppTag(form);
183+
}
184+
insertedTags.add(idTag);
185+
}
186+
187+
return insertedTags;
188+
}
189+
190+
private static String getRandomParentIdTag(String current, List<String> source) {
191+
if (source.isEmpty()) {
192+
return null;
193+
}
194+
if (ThreadLocalRandom.current().nextBoolean()) {
195+
String parent = source.get(ThreadLocalRandom.current().nextInt(0, source.size()));
196+
if (!Objects.equals(parent, current)) {
197+
return parent;
198+
}
199+
}
200+
return null;
201+
}
202+
203+
private static LocalDateTime getRandomExpiry() {
204+
if (ThreadLocalRandom.current().nextBoolean()) {
205+
return LocalDateTime.now().plusDays(ThreadLocalRandom.current().nextInt(1, 365));
206+
}
207+
return null;
208+
}
209+
}

0 commit comments

Comments
 (0)