• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SpiNNakerManchester / JavaSpiNNaker / 6310285782

26 Sep 2023 08:47AM UTC coverage: 36.367% (-0.5%) from 36.866%
6310285782

Pull #658

github

dkfellows
Merge branch 'master' into java-17
Pull Request #658: Update Java version to 17 and JEE to 9

1675 of 1675 new or added lines in 266 files covered. (100.0%)

8368 of 23010 relevant lines covered (36.37%)

0.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

39.44
/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteStorage.java
1
/*
2
 * Copyright (c) 2018 The University of Manchester
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package uk.ac.manchester.spinnaker.storage.sqlite;
17

18
import static org.slf4j.LoggerFactory.getLogger;
19
import static org.sqlite.SQLiteErrorCode.SQLITE_BUSY;
20
import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.COOKIE;
21
import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.GET_PROXY_INFORMATION;
22
import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.HEADER;
23
import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.PROXY_URI;
24
import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.SPALLOC;
25
import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.SPALLOC_URI;
26

27
import java.sql.Connection;
28
import java.sql.PreparedStatement;
29
import java.sql.SQLException;
30
import java.util.HashMap;
31

32
import org.slf4j.Logger;
33
import org.sqlite.SQLiteException;
34

35
import com.google.errorprone.annotations.concurrent.GuardedBy;
36

37
import uk.ac.manchester.spinnaker.storage.DatabaseAPI;
38
import uk.ac.manchester.spinnaker.storage.DatabaseEngine;
39
import uk.ac.manchester.spinnaker.storage.ProxyInformation;
40
import uk.ac.manchester.spinnaker.storage.StorageException;
41

42
/**
43
 * Wrapper that handles how to perform transactions.
44
 *
45
 * @author Donal Fellows
46
 * @param <APIType>
47
 *            The type of the connections used inside this class. Probably the
48
 *            type of the concrete subclass of this class.
49
 */
50
abstract sealed class SQLiteStorage<APIType extends DatabaseAPI>
51
                implements DatabaseAPI
52
                permits SQLiteBufferStorage, SQLiteDataSpecStorage {
53
        private static final Logger log = getLogger(SQLiteStorage.class);
1✔
54

55
        @GuardedBy("itself")
56
        private final DatabaseEngine<APIType> db;
57

58
        /**
59
         * @param db
60
         *            The source of database connections.
61
         * @see Connection
62
         */
63
        protected SQLiteStorage(DatabaseEngine<APIType> db) {
1✔
64
                this.db = db;
1✔
65
        }
1✔
66

67
        /**
68
         * Set the arguments to a prepared statement. Convenience method.
69
         *
70
         * @param statement
71
         *            The prepared statement
72
         * @param args
73
         *            The arguments to set. Each may be {@link Boolean},
74
         *            {@link Integer}, {@link Long}, {@link Double}, {@link String},
75
         *            {@code byte[]}, or {@code null}.
76
         * @throws SQLException
77
         *             If the prepared statement is not currently open.
78
         */
79
        protected static void setArguments(PreparedStatement statement,
80
                        Object... args) throws SQLException {
81
                statement.clearParameters();
1✔
82
                int idx = 1;
1✔
83
                for (var o : args) {
1✔
84
                        statement.setObject(idx, o);
1✔
85
                        idx++;
1✔
86
                }
87
        }
1✔
88

89
        /**
90
         * A wrapped piece of code that produces a result.
91
         *
92
         * @param <T>
93
         *            The type of the result of the call
94
         * @author Donal Fellows
95
         */
96
        @FunctionalInterface
97
        interface CallWithResult<T> {
98
                /**
99
                 * The wrapped code.
100
                 *
101
                 * @param conn
102
                 *            The connection that has a running transaction on it.
103
                 * @return The result of the code.
104
                 * @throws SQLException
105
                 *             If anything goes wrong.
106
                 */
107
                T call(Connection conn) throws SQLException;
108
        }
109

110
        /**
111
         * A wrapped piece of code that doesn't produce a result.
112
         *
113
         * @author Donal Fellows
114
         */
115
        @FunctionalInterface
116
        interface CallWithoutResult {
117
                /**
118
                 * The wrapped code.
119
                 *
120
                 * @param conn
121
                 *            The connection that has a running transaction on it.
122
                 * @throws SQLException
123
                 *             If anything goes wrong.
124
                 */
125
                void call(Connection conn) throws SQLException;
126
        }
127

128
        /**
129
         * Wrapper for applying a transaction correctly.
130
         *
131
         * @param <T>
132
         *            The type of the result of the wrapped call.
133
         * @param call
134
         *            What is wrapped. Produces a result.
135
         * @param actionDescription
136
         *            Extra message to use with wrapping exception.
137
         * @return The value returned by the call
138
         * @throws StorageException
139
         *             If anything goes wrong
140
         */
141
        final <T> T callR(CallWithResult<T> call, String actionDescription)
142
                        throws StorageException {
143
                synchronized (db) {
1✔
144
                        try (var conn = db.getConnection()) {
1✔
145
                                startTransaction(conn);
1✔
146
                                try {
147
                                        var result = call.call(conn);
1✔
148
                                        conn.commit();
1✔
149
                                        return result;
1✔
150
                                } catch (Exception e) {
×
151
                                        conn.rollback();
×
152
                                        throw e;
×
153
                                }
154
                        } catch (SQLException | IllegalStateException e) {
1✔
155
                                throw new StorageException("while " + actionDescription, e);
×
156
                        }
157
                }
158
        }
159

160
        /**
161
         * Wrapper for applying a transaction correctly.
162
         *
163
         * @param call
164
         *            What is wrapped. Produces no result.
165
         * @param actionDescription
166
         *            Extra message to use with wrapping exception.
167
         * @throws StorageException
168
         *             If anything goes wrong
169
         */
170
        final void callV(CallWithoutResult call, String actionDescription)
171
                        throws StorageException {
172
                synchronized (db) {
1✔
173
                        try (var conn = db.getConnection()) {
1✔
174
                                startTransaction(conn);
1✔
175
                                try {
176
                                        call.call(conn);
1✔
177
                                        conn.commit();
1✔
178
                                        return;
1✔
179
                                } catch (Exception e) {
×
180
                                        conn.rollback();
×
181
                                        throw e;
×
182
                                }
183
                        } catch (SQLException | IllegalStateException e) {
1✔
184
                                throw new StorageException("while " + actionDescription, e);
×
185
                        }
186
                }
187
        }
188

189
        private static final int TRIES = 50;
190

191
        private void startTransaction(Connection conn) throws SQLException {
192
                var code = SQLITE_BUSY;
1✔
193
                for (int i = 0; i < TRIES; i++) {
1✔
194
                        try {
195
                                conn.setAutoCommit(false);
1✔
196
                                return;
1✔
197
                        } catch (SQLiteException e) {
×
198
                                switch (e.getResultCode()) {
×
199
                                case SQLITE_BUSY, SQLITE_BUSY_RECOVERY, SQLITE_BUSY_SNAPSHOT,
200
                                                SQLITE_BUSY_TIMEOUT -> {
201
                                        log.debug("database busy; trying to relock");
×
202
                                        code = e.getResultCode();
×
203
                                }
×
204
                                default -> throw e;
×
205
                                }
206
                        }
207
                }
208
                throw new SQLiteException("database very busy", code);
×
209
        }
210

211
        @Override
212
        public ProxyInformation getProxyInformation() throws StorageException {
213
                return callR(conn -> getProxyInfo(conn), "get proxy");
×
214
        }
215

216
        /**
217
         * Get the proxy information from a database.
218
         *
219
         * @param conn
220
         *            The connection to read the data from.
221
         * @return The proxy information.
222
         * @throws SQLException
223
         *             If there is an error reading the database.
224
         * @throws Unreachable
225
         *             If a bad row is retrieved; should be unreachable if SQL is
226
         *             synched to code.
227
         */
228
        @SuppressWarnings("checkstyle:InnerAssignment") // Rule is misapplying
229
        private ProxyInformation getProxyInfo(Connection conn) throws SQLException {
230
                String spallocUri = null;
×
231
                String jobUri = null;
×
232
                var headers = new HashMap<String, String>();
×
233
                var cookies = new HashMap<String, String>();
×
234

235
                try (var s = conn.prepareStatement(GET_PROXY_INFORMATION);
×
236
                                var rs = s.executeQuery()) {
×
237
                        while (rs.next()) {
×
238
                                var kind = rs.getString("kind");
×
239
                                var name = rs.getString("name");
×
240
                                var value = rs.getString("value");
×
241
                                if (kind == null || name == null || value == null) {
×
242
                                        continue;
×
243
                                }
244
                                switch (kind) {
×
245
                                case SPALLOC -> {
246
                                        switch (name) {
×
247
                                        case SPALLOC_URI -> spallocUri = value;
×
248
                                        case PROXY_URI -> jobUri = value;
×
249
                                        default -> throw new Unreachable();
×
250
                                        }
251
                                }
×
252

253
                                case COOKIE -> cookies.put(name, value);
×
254
                                case HEADER -> headers.put(name, value);
×
255
                                default -> throw new Unreachable();
×
256
                                }
257
                        }
×
258
                }
259
                // If we don't have all pieces of info, we can't talk to the proxy
260
                if (spallocUri == null || jobUri == null) {
×
261
                        return null;
×
262
                }
263
                return new ProxyInformation(spallocUri, jobUri, headers, cookies);
×
264
        }
265

266
        /** Thrown when an unreachable state is reached. */
267
        private static class Unreachable extends IllegalStateException {
268
                private static final long serialVersionUID = 1L;
269

270
                Unreachable() {
271
                        super("unreachable reached");
×
272
                }
×
273
        }
274
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc