DbEngine2.java

1
package com.renomad.minum.database;
2
3
import com.renomad.minum.state.Context;
4
5
import java.io.IOException;
6
import java.nio.charset.StandardCharsets;
7
import java.nio.file.Files;
8
import java.nio.file.Path;
9
import java.util.*;
10
import java.util.concurrent.atomic.AtomicInteger;
11
import java.util.concurrent.atomic.AtomicLong;
12
import java.util.concurrent.locks.ReentrantLock;
13
import java.util.function.Function;
14
import java.util.stream.Stream;
15
16
import static com.renomad.minum.utils.Invariants.mustBeFalse;
17
import static com.renomad.minum.utils.Invariants.mustBeTrue;
18
19
/**
20
 * a memory-based disk-persisted database class.
21
 *
22
 * <p>
23
 *     Engine 2 is a database engine that improves on the performance from the first
24
 *     database provided by Minum. It does this by using different strategies for disk persistence.
25
 * </p>
26
 * <p>
27
 *     The mental model of the previous Minum database has been an in-memory data
28
 *     structure in which every change is eventually written to its own file on disk for
29
 *     persistence.  Data changes affect just their relevant files.  The benefit of this approach is
30
 *     extreme simplicity. It requires very little code, relying as it does on the operating system's file capabilities.
31
 * </p>
32
 * <p>
33
 *     However, there are two performance problems with this approach.  First is when the
34
 *     data changes are arriving at a high rate.  In that situation, the in-memory portion keeps up to date,
35
 *     but the disk portion may lag by minutes.  The second problem is start-up time.  When
36
 *     the database starts, it reads files into memory.  The database can read about 6,000
37
 *     files a second in the best case.  If there are a million data items, it would take
38
 *     about 160 seconds to load it into memory, which is far too long.
39
 * </p>
40
 * <p>
41
 *      The new approach to disk persistence is to append each change to a file.  Append-only file
42
 *      changes can be very fast.  These append files are eventually consolidated into files
43
 *      partitioned by their index - data with indexes between 1 and 1000 go into one file, between
44
 *      1001 and 2000 go into another, and so on.
45
 *  </p>
46
 *  <p>
47
 *      Startup is magnitudes faster by this approach.  What took the previous database 160 seconds
48
 *      to load requires only 2 seconds. Writes to disk are also faster. What would have taken
49
 *      several minutes to write should only take a few seconds now.
50
 *  </p>
51
 *  <p>
52
 *      This new approach uses a different file structure than the previous. If it is
53
 *      desired to use the new engine on existing data, it is possible to convert the old
54
 *      data format to the new.  Construct an instance of the new engine, pointing
55
 *      at the same name as the previous, and it will convert the data.  If the previous
56
 *      call looked like this:
57
 *  </p>
58
 *  <code>
59
 *  Db<Photograph> photoDb = context.getDb("photos", Photograph.EMPTY);
60
 *  </code>
61
 *  <p>
62
 *  Then converting to the new database is just replacing it with the following
63
 *  line. <b>Please, backup your database before this change.</b>
64
 *  </p>
65
 *  <p>
66
 * <code>
67
 *     DbEngine2<Photograph> photoDb = context.getDb2("photos", Photograph.EMPTY);
68
 * </code>
69
 *  </p>
70
 *  <p>
71
 *     Once the new engine starts up, it will notice the old file structure and convert it
72
 *     over.  The methods and behaviors are mostly the same between the old and new engines, so the
73
 *     update should be straightforward.
74
 * </p>
75
 * <p>
76
 *     (By the way, it *is* possible to convert back to the old file structure,
77
 *     by starting the database the old way again.  Just be aware that each time the
78
 *     files are converted, it takes longer than normal to start the database)
79
 * </p>
80
 * <p>
81
 *     However, something to note is that using the old database is still fine in many cases,
82
 *     particularly for prototypes or systems which do not contain large amounts of data. If
83
 *     your system is working fine, there is no need to change things.
84
 * </p>
85
 *
86
 * @param <T> the type of data we'll be persisting (must extend from {@link DbData})
87
 */
88
public final class DbEngine2<T extends DbData<?>> extends AbstractDb<T> {
89
90
    private final ReentrantLock loadDataLock;
91
    private final ReentrantLock consolidateLock;
92
    private final ReentrantLock writeLock;
93
    int maxLinesPerAppendFile;
94
    boolean hasLoadedData;
95
    final DatabaseAppender databaseAppender;
96
    private final DatabaseConsolidator databaseConsolidator;
97
98
    /**
99
     * Here we track the number of appends we have made.  Once it hits
100
     * a certain number, we will kick off a consolidation in a thread
101
     */
102
    final AtomicInteger appendCount = new AtomicInteger(0);
103
104
    /**
105
     * Used to determine whether to kick off consolidation.  If it is
106
     * already running, we don't want to kick it off again. This would
107
     * only affect us if we are updating the database very fast.
108
     */
109
    boolean consolidationIsRunning;
110
111
    /**
112
     * Constructs an in-memory disk-persisted database.
113
     * Loading of data from disk happens at the first invocation of any command
114
     * changing or requesting data, such as {@link #write(DbData)}, {@link #delete(DbData)},
115
     * or {@link #values()}.  See the private method loadData() for details.
116
     * @param dbDirectory this uniquely names your database, and also sets the directory
117
     *                    name for this data.  The expected use case is to name this after
118
     *                    the data in question.  For example, "users", or "accounts".
119
     * @param context used to provide important state data to several components
120
     * @param instance an instance of the {@link DbData} object relevant for use in this database. Note
121
     *                 that each database (that is, each instance of this class), focuses on just one
122
     *                 data, which must be an implementation of {@link DbData}.
123
     */
124
    public DbEngine2(Path dbDirectory, Context context, T instance) {
125
        super(dbDirectory, context, instance);
126
127
        this.databaseConsolidator = new DatabaseConsolidator(dbDirectory, context);
128
        try {
129
            this.databaseAppender = new DatabaseAppender(dbDirectory, context);
130
        } catch (IOException e) {
131
            throw new DbException("Error while initializing DatabaseAppender in DbEngine2", e);
132
        }
133
        this.loadDataLock = new ReentrantLock();
134
        this.consolidateLock = new ReentrantLock();
135
        this.writeLock = new ReentrantLock();
136
        this.maxLinesPerAppendFile = context.getConstants().maxAppendCount;
137
    }
138
139
    /**
140
     * Write data to the database.  Use an index of 0 to store new data, and a positive
141
     * non-zero value to update data.
142
     * <p><em>
143
     *     Example of adding new data to the database:
144
     * </p></em>
145
     * {@snippet :
146
     *          final var newSalt = StringUtils.generateSecureRandomString(10);
147
     *          final var hashedPassword = CryptoUtils.createPasswordHash(newPassword, newSalt);
148
     *          final var newUser = new User(0L, newUsername, hashedPassword, newSalt);
149
     *          userDb.write(newUser);
150
     * }
151
     * <p><em>
152
     *     Example of updating data:
153
     * </p></em>
154
     * {@snippet :
155
     *         // write the updated salted password to the database
156
     *         final var updatedUser = new User(
157
     *                 user().getIndex(),
158
     *                 user().getUsername(),
159
     *                 hashedPassword,
160
     *                 newSalt);
161
     *         userDb.write(updatedUser);
162
     * }
163
     *
164
     * @param newData the data we are writing
165
     * @return the data with its new index assigned.
166
     * @throws DbException if there is a failure to write
167
     */
168
    @Override
169
    public T write(T newData) {
170 2 1. write : changed conditional boundary → KILLED
2. write : negated conditional → KILLED
        if (newData.getIndex() < 0) throw new DbException("Negative indexes are disallowed");
171
        // load data if needed
172 2 1. write : removed call to com/renomad/minum/database/DbEngine2::loadData → KILLED
2. write : negated conditional → KILLED
        if (!hasLoadedData) loadData();
173
174 1 1. write : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
        writeLock.lock();
175
        try {
176
            boolean newElementCreated = processDataIndex(newData);
177 1 1. write : removed call to com/renomad/minum/database/DbEngine2::writeToDisk → KILLED
            writeToDisk(newData);
178 1 1. write : removed call to com/renomad/minum/database/DbEngine2::writeToMemory → KILLED
            writeToMemory(newData, newElementCreated);
179
        } catch (IOException ex) {
180
           throw new DbException("failed to write data " + newData, ex);
181
        } finally {
182 1 1. write : removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT
            writeLock.unlock();
183
        }
184
185
        // returning the data at this point is the most convenient
186
        // way users will have access to the new index of the data.
187 1 1. write : replaced return value with null for com/renomad/minum/database/DbEngine2::write → KILLED
        return newData;
188
    }
189
190
191
    private void writeToDisk(T newData) throws IOException {
192
        logger.logTrace(() -> String.format("writing data to disk: %s", newData));
193
        String serializedData = newData.serialize();
194
        mustBeFalse(serializedData == null || serializedData.isBlank(),
195
                "the serialized form of data must not be blank. " +
196
                        "Is the serialization code written properly? Our datatype: " + emptyInstance);
197
        databaseAppender.appendToDatabase(DatabaseChangeAction.UPDATE, serializedData);
198
        appendCount.incrementAndGet();
199
        consolidateIfNecessary();
200
    }
201
202
    /**
203
     * If the append count is large enough, we will call the
204
     * consolidation method on the DatabaseConsolidator and
205
     * reset the append count to 0.
206
     */
207
    boolean consolidateIfNecessary() {
208 3 1. consolidateIfNecessary : changed conditional boundary → KILLED
2. consolidateIfNecessary : negated conditional → KILLED
3. consolidateIfNecessary : negated conditional → KILLED
        if (appendCount.get() > maxLinesPerAppendFile && !consolidationIsRunning) {
209 1 1. consolidateIfNecessary : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
            consolidateLock.lock(); // block threads here if multiple are trying to get in - only one gets in at a time
210
            try {
211 1 1. consolidateIfNecessary : removed call to com/renomad/minum/database/DbEngine2::consolidateInnerCode → KILLED
                consolidateInnerCode();
212
            } finally {
213 1 1. consolidateIfNecessary : removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT
                consolidateLock.unlock();
214
            }
215 1 1. consolidateIfNecessary : replaced boolean return with false for com/renomad/minum/database/DbEngine2::consolidateIfNecessary → KILLED
            return true;
216
        }
217 1 1. consolidateIfNecessary : replaced boolean return with true for com/renomad/minum/database/DbEngine2::consolidateIfNecessary → KILLED
        return false;
218
    }
219
220
    /**
221
     * This code is only called in production from {@link #consolidateIfNecessary()},
222
     * and is necessarily protected by mutex locks.  However, it is provided
223
     * here as its own method for ease of testing.
224
     */
225
    void consolidateInnerCode() {
226 3 1. consolidateInnerCode : negated conditional → KILLED
2. consolidateInnerCode : negated conditional → KILLED
3. consolidateInnerCode : changed conditional boundary → KILLED
        if (appendCount.get() > maxLinesPerAppendFile && !consolidationIsRunning) {
227
            context.getExecutorService().submit(() -> {
228
                try {
229
                    consolidationIsRunning = true;
230 1 1. lambda$consolidateInnerCode$2 : removed call to com/renomad/minum/database/DatabaseConsolidator::consolidate → KILLED
                    databaseConsolidator.consolidate();
231
                    consolidationIsRunning = false;
232
                } catch (Exception e) {
233
                    logger.logAsyncError(() -> "Error during consolidation: " + e);
234
                }
235
            });
236 1 1. consolidateInnerCode : removed call to java/util/concurrent/atomic/AtomicInteger::set → KILLED
            appendCount.set(0);
237
        }
238
    }
239
240
    /**
241
     * Delete data
242
     * <p><em>Example:</p></em>
243
     * {@snippet :
244
     *      userDb.delete(user);
245
     * }
246
     * @param dataToDelete the data we are serializing and writing
247
     * @throws DbException if there is a failure to delete
248
     */
249
    @Override
250
    public void delete(T dataToDelete) {
251
        // load data if needed
252 2 1. delete : negated conditional → KILLED
2. delete : removed call to com/renomad/minum/database/DbEngine2::loadData → KILLED
        if (!hasLoadedData) loadData();
253
254 1 1. delete : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
        writeLock.lock();
255
        try {
256 1 1. delete : removed call to com/renomad/minum/database/DbEngine2::deleteFromDisk → KILLED
            deleteFromDisk(dataToDelete);
257 1 1. delete : removed call to com/renomad/minum/database/DbEngine2::deleteFromMemory → KILLED
            deleteFromMemory(dataToDelete);
258
        } catch (IOException ex) {
259
            throw new DbException("failed to delete data " + dataToDelete, ex);
260
        } finally {
261 1 1. delete : removed call to java/util/concurrent/locks/ReentrantLock::unlock → SURVIVED
            writeLock.unlock();
262
        }
263
    }
264
265
    private void deleteFromDisk(T dataToDelete) throws IOException {
266
        logger.logTrace(() -> String.format("deleting data from disk: %s", dataToDelete));
267
        databaseAppender.appendToDatabase(DatabaseChangeAction.DELETE, dataToDelete.serialize());
268
        appendCount.incrementAndGet();
269
        consolidateIfNecessary();
270
    }
271
272
273
    /**
274
     * Tells the database to load its data into memory immediately rather
275
     * than wait for a command that would require data (like {@link #write(DbData)},
276
     * {@link #delete(DbData)}, or {@link #values()}). This may be valuable
277
     * in cases where the developer wants greater control over the timing - such
278
     * as getting the data loaded into memory immediately at program start.
279
     */
280
    private void loadDataFromDisk() throws IOException {
281
        // if we find the "index.ddps" file, it means we are looking at an old
282
        // version of the database.  Update it to the new version, and then afterwards
283
        // remove the old version files.
284 1 1. loadDataFromDisk : negated conditional → KILLED
        if (Files.exists(dbDirectory.resolve("index.ddps"))) {
285 1 1. loadDataFromDisk : removed call to com/renomad/minum/database/DbFileConverter::convertClassicFolderStructureToDbEngine2Form → KILLED
            new DbFileConverter(context, dbDirectory).convertClassicFolderStructureToDbEngine2Form();
286
        }
287
288 1 1. loadDataFromDisk : removed call to com/renomad/minum/utils/FileUtils::makeDirectory → KILLED
        fileUtils.makeDirectory(dbDirectory);
289
        // if there are any remnant items in the current append-only file, move them
290
        // to a new file
291
        databaseAppender.saveOffCurrentDataToReadyFolder();
292 1 1. loadDataFromDisk : removed call to com/renomad/minum/database/DatabaseAppender::flush → TIMED_OUT
        databaseAppender.flush();
293
294
        // consolidate whatever files still exist in the append logs
295 1 1. loadDataFromDisk : removed call to com/renomad/minum/database/DatabaseConsolidator::consolidate → KILLED
        databaseConsolidator.consolidate();
296
297
        // load the data into memory
298 1 1. loadDataFromDisk : removed call to com/renomad/minum/database/DbEngine2::walkAndLoad → KILLED
        walkAndLoad(dbDirectory);
299
300 1 1. loadDataFromDisk : negated conditional → KILLED
        if (data.isEmpty()) {
301
            this.index = new AtomicLong(1);
302
        } else {
303 1 1. loadDataFromDisk : Replaced long addition with subtraction → TIMED_OUT
            var initialIndex = Collections.max(data.keySet()) + 1L;
304
            this.index = new AtomicLong(initialIndex);
305
        }
306
    }
307
308
    /**
309
     * Loops through each line of data in the consolidated data files,
310
     * converting each to its strongly-typed form and adding to the database
311
     */
312
    void walkAndLoad(Path dbDirectory) {
313
        List<String> consolidatedFiles = new ArrayList<>(
314
                List.of(Objects.requireNonNull(dbDirectory.resolve("consolidated_data").toFile().list())));
315
316
        // if there aren't any files, bail out
317 1 1. walkAndLoad : negated conditional → KILLED
        if (consolidatedFiles.isEmpty()) return;
318
319
        // sort
320 1 1. walkAndLoad : removed call to java/util/List::sort → TIMED_OUT
        consolidatedFiles.sort(Comparator.comparingLong(DbEngine2::parseConsolidatedFileName));
321
322
        for (String fileName : consolidatedFiles) {
323
            logger.logDebug(() -> "Processing database file: " + fileName);
324
            Path consolidatedDataFile = dbDirectory.resolve("consolidated_data").resolve(fileName);
325
326
            // By using a lazy stream, we are able to read each item from the file into
327
            // memory without needing to read the whole file contents into memory at once,
328
            // thus avoiding requiring a great amount of memory
329
            try(Stream<String> fileStream = Files.lines(consolidatedDataFile, StandardCharsets.US_ASCII)) {
330 2 1. walkAndLoad : removed call to java/util/stream/Stream::forEach → KILLED
2. lambda$walkAndLoad$5 : removed call to com/renomad/minum/database/DbEngine2::readAndDeserialize → KILLED
                fileStream.forEach(line -> readAndDeserialize(line, fileName));
331
            } catch (Exception e) {
332
                throw new DbException(e);
333
            }
334
        }
335
    }
336
337
    /**
338
     * Given a file like 1_to_1000 or 1001_to_2000, extract out the
339
     * beginning index (i.e. 1, or 1001).
340
     */
341
    static long parseConsolidatedFileName(String file) {
342
        int index = file.indexOf("_to_");
343 1 1. parseConsolidatedFileName : negated conditional → KILLED
        if (index == -1) {
344
            throw new DbException("Consolidated filename was invalid: " + file);
345
        }
346 1 1. parseConsolidatedFileName : replaced long return with 0 for com/renomad/minum/database/DbEngine2::parseConsolidatedFileName → TIMED_OUT
        return Long.parseLong(file, 0, index, 10);
347
    }
348
349
    /**
350
     * Converts a serialized string to a strongly-typed data structure
351
     * and adds it to the database.
352
     */
353
    void readAndDeserialize(String lineOfData, String fileName) {
354
        try {
355
            @SuppressWarnings("unchecked")
356
            T deserializedData = (T) emptyInstance.deserialize(lineOfData);
357
            mustBeTrue(deserializedData != null, "deserialization of " + emptyInstance +
358
                    " resulted in a null value. Was the serialization method implemented properly?");
359
360
            // put the data into the in-memory data structure
361
            data.put(deserializedData.getIndex(), deserializedData);
362 1 1. readAndDeserialize : removed call to com/renomad/minum/database/DbEngine2::addToIndexes → KILLED
            addToIndexes(deserializedData);
363
364
        } catch (Exception e) {
365
            throw new DbException("Failed to deserialize " + lineOfData + " with data (\"" + fileName + "\"). Caused by: " + e);
366
        }
367
    }
368
369
370
    /**
371
     * This is what loads the data from disk the
372
     * first time someone needs it.  Because it is
373
     * locked, only one thread can enter at
374
     * a time.  The first one in will load the data,
375
     * and the second will encounter a branch which skips loading.
376
     */
377
    @Override
378
    public void loadData() {
379 1 1. loadData : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
        loadDataLock.lock(); // block threads here if multiple are trying to get in - only one gets in at a time
380
        try {
381 1 1. loadData : negated conditional → KILLED
            if (!hasLoadedData) {
382 1 1. loadData : removed call to com/renomad/minum/database/DbEngine2::loadDataFromDisk → KILLED
                loadDataFromDisk();
383
            }
384
            hasLoadedData = true;
385
        } catch (Exception ex) {
386
            throw new DbException("Failed to load data from disk.", ex);
387
        } finally {
388 1 1. loadData : removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT
            loadDataLock.unlock();
389
        }
390
    }
391
392
    /**
393
     * This method provides read capability for the values of a database.
394
     * <br>
395
     * The returned collection is a read-only view over the data, through {@link Collections#unmodifiableCollection(Collection)}
396
     *
397
     * <p><em>Example:</em></p>
398
     * {@snippet :
399
     * boolean doesUserAlreadyExist(String username) {
400
     *     return userDb.values().stream().anyMatch(x -> x.getUsername().equals(username));
401
     * }
402
     * }
403
     */
404
    @Override
405
    public Collection<T> values() {
406
        // load data if needed
407 2 1. values : negated conditional → KILLED
2. values : removed call to com/renomad/minum/database/DbEngine2::loadData → KILLED
        if (!hasLoadedData) loadData();
408
409 1 1. values : replaced return value with Collections.emptyList for com/renomad/minum/database/DbEngine2::values → KILLED
        return Collections.unmodifiableCollection(data.values());
410
    }
411
412
    @Override
413
    public boolean registerIndex(String indexName, Function<T, String> keyObtainingFunction) {
414 1 1. registerIndex : negated conditional → KILLED
        if (hasLoadedData) {
415
            throw new DbException("This method must be run before the database loads data from disk.  Typically, " +
416
                    "it should be run immediately after the database is created.  See this method's documentation");
417
        }
418 2 1. registerIndex : replaced boolean return with true for com/renomad/minum/database/DbEngine2::registerIndex → SURVIVED
2. registerIndex : replaced boolean return with false for com/renomad/minum/database/DbEngine2::registerIndex → KILLED
        return super.registerIndex(indexName, keyObtainingFunction);
419
    }
420
421
422
    @Override
423
    public Collection<T> getIndexedData(String indexName, String key) {
424
        // load data if needed
425 2 1. getIndexedData : negated conditional → KILLED
2. getIndexedData : removed call to com/renomad/minum/database/DbEngine2::loadData → KILLED
        if (!hasLoadedData) loadData();
426 1 1. getIndexedData : replaced return value with Collections.emptyList for com/renomad/minum/database/DbEngine2::getIndexedData → KILLED
        return super.getIndexedData(indexName, key);
427
    }
428
429
    /**
430
     * This command calls {@link DatabaseAppender#flush()}, which will
431
     * force any in-memory-buffered data to be written to disk.  This is
432
     * not commonly necessary to call for business purposes, but tests
433
     * may require it if you want to be absolutely sure the data is written
434
     * to disk at a particular moment.
435
     */
436
    public void flush() {
437 1 1. flush : removed call to com/renomad/minum/database/DatabaseAppender::flush → TIMED_OUT
        this.databaseAppender.flush();
438
    }
439
440
    /**
441
     * This is here to match the contract of {@link Db}
442
     * but all it does is tell the interior file writer
443
     * to write its data to disk.
444
     */
445
    @Override
446
    public void stop() {
447 1 1. stop : removed call to com/renomad/minum/database/DbEngine2::flush → TIMED_OUT
        flush();
448
    }
449
450
    /**
451
     * No real difference to {@link #stop()} but here
452
     * to have a similar contract to {@link Db}
453
     */
454
    @Override
455
    public void stop(int count, int sleepTime) {
456 1 1. stop : removed call to com/renomad/minum/database/DbEngine2::flush → TIMED_OUT
        flush();
457
    }
458
}

Mutations

170

1.1
Location : write
Killed by : com.renomad.minum.database.DbEngine2Tests.test_FailureDuringWrite(com.renomad.minum.database.DbEngine2Tests)
changed conditional boundary → KILLED

2.2
Location : write
Killed by : com.renomad.minum.database.DbEngine2Tests.test_FailureDuringWrite(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

172

1.1
Location : write
Killed by : com.renomad.minum.database.DbEngine2Tests.test_EdgeCase_RegisteringIndexTooLate(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbEngine2::loadData → KILLED

2.2
Location : write
Killed by : com.renomad.minum.database.DbEngine2Tests.test_EdgeCase_RegisteringIndexTooLate(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

174

1.1
Location : write
Killed by : com.renomad.minum.database.DbEngine2Tests.test_FailureDuringWrite(com.renomad.minum.database.DbEngine2Tests)
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

177

1.1
Location : write
Killed by : com.renomad.minum.database.DbEngine2Tests.test_FailureDuringWrite(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbEngine2::writeToDisk → KILLED

178

1.1
Location : write
Killed by : com.renomad.minum.database.DbEngine2Tests.testSearchUtility(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbEngine2::writeToMemory → KILLED

182

1.1
Location : write
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT

187

1.1
Location : write
Killed by : com.renomad.minum.database.DbEngine2Tests.testSearchUtility(com.renomad.minum.database.DbEngine2Tests)
replaced return value with null for com/renomad/minum/database/DbEngine2::write → KILLED

208

1.1
Location : consolidateIfNecessary
Killed by : com.renomad.minum.database.DbEngine2Tests.test_ConsolidateIfNecessary(com.renomad.minum.database.DbEngine2Tests)
changed conditional boundary → KILLED

2.2
Location : consolidateIfNecessary
Killed by : com.renomad.minum.database.DbEngine2Tests.test_ConsolidateIfNecessary(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

3.3
Location : consolidateIfNecessary
Killed by : com.renomad.minum.database.DbEngine2Tests.test_ConsolidateIfNecessary(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

209

1.1
Location : consolidateIfNecessary
Killed by : com.renomad.minum.database.DbEngine2Tests.test_ConsolidateIfNecessary(com.renomad.minum.database.DbEngine2Tests)
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

211

1.1
Location : consolidateIfNecessary
Killed by : com.renomad.minum.database.DbEngine2Tests.test_FailureDuringConsolidation(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbEngine2::consolidateInnerCode → KILLED

213

1.1
Location : consolidateIfNecessary
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT

215

1.1
Location : consolidateIfNecessary
Killed by : com.renomad.minum.database.DbEngine2Tests.test_ConsolidateIfNecessary(com.renomad.minum.database.DbEngine2Tests)
replaced boolean return with false for com/renomad/minum/database/DbEngine2::consolidateIfNecessary → KILLED

217

1.1
Location : consolidateIfNecessary
Killed by : com.renomad.minum.database.DbEngine2Tests.test_ConsolidateIfNecessary(com.renomad.minum.database.DbEngine2Tests)
replaced boolean return with true for com/renomad/minum/database/DbEngine2::consolidateIfNecessary → KILLED

226

1.1
Location : consolidateInnerCode
Killed by : com.renomad.minum.database.DbEngine2Tests.test_ConsolidateInnerCode(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

2.2
Location : consolidateInnerCode
Killed by : com.renomad.minum.database.DbEngine2Tests.test_ConsolidateInnerCode(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

3.3
Location : consolidateInnerCode
Killed by : com.renomad.minum.database.DbEngine2Tests.test_ConsolidateInnerCode(com.renomad.minum.database.DbEngine2Tests)
changed conditional boundary → KILLED

230

1.1
Location : lambda$consolidateInnerCode$2
Killed by : com.renomad.minum.database.DbEngine2Tests.test_FailureDuringConsolidation(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DatabaseConsolidator::consolidate → KILLED

236

1.1
Location : consolidateInnerCode
Killed by : com.renomad.minum.database.DbEngine2Tests.test_ConsolidateInnerCode(com.renomad.minum.database.DbEngine2Tests)
removed call to java/util/concurrent/atomic/AtomicInteger::set → KILLED

252

1.1
Location : delete
Killed by : com.renomad.minum.database.DbEngine2Tests.test_Db_Write_and_Read(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

2.2
Location : delete
Killed by : com.renomad.minum.database.DbEngine2Tests.test_Db_Write_and_Read(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbEngine2::loadData → KILLED

254

1.1
Location : delete
Killed by : com.renomad.minum.database.DbEngine2Tests.test_FailureDuringDelete(com.renomad.minum.database.DbEngine2Tests)
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

256

1.1
Location : delete
Killed by : com.renomad.minum.database.DbEngine2Tests.test_FailureDuringDelete(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbEngine2::deleteFromDisk → KILLED

257

1.1
Location : delete
Killed by : com.renomad.minum.database.DbEngine2Tests.testIndexesOnPartitionedData(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbEngine2::deleteFromMemory → KILLED

261

1.1
Location : delete
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::unlock → SURVIVED
Covering tests

284

1.1
Location : loadDataFromDisk
Killed by : com.renomad.minum.database.DbEngine2Tests.test_NegativeCase_NoIndex(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

285

1.1
Location : loadDataFromDisk
Killed by : com.renomad.minum.database.DbEngine2Tests.test_ConvertingDatabase_Db_To_DbEngine2(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbFileConverter::convertClassicFolderStructureToDbEngine2Form → KILLED

288

1.1
Location : loadDataFromDisk
Killed by : com.renomad.minum.database.DbEngine2Tests.testIndexSpeedDifference(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/utils/FileUtils::makeDirectory → KILLED

292

1.1
Location : loadDataFromDisk
Killed by : none
removed call to com/renomad/minum/database/DatabaseAppender::flush → TIMED_OUT

295

1.1
Location : loadDataFromDisk
Killed by : com.renomad.minum.database.DbEngine2Tests.test_LoadingData_NegativeCase(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DatabaseConsolidator::consolidate → KILLED

298

1.1
Location : loadDataFromDisk
Killed by : com.renomad.minum.database.DbEngine2Tests.test_Initialize_readAndDeserialize_NegativeCase(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbEngine2::walkAndLoad → KILLED

300

1.1
Location : loadDataFromDisk
Killed by : com.renomad.minum.database.DbEngine2Tests.test_NegativeCase_NoIndex(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

303

1.1
Location : loadDataFromDisk
Killed by : none
Replaced long addition with subtraction → TIMED_OUT

317

1.1
Location : walkAndLoad
Killed by : com.renomad.minum.database.DbEngine2Tests.test_WalkAndLoad_NegativeCase(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

320

1.1
Location : walkAndLoad
Killed by : none
removed call to java/util/List::sort → TIMED_OUT

330

1.1
Location : walkAndLoad
Killed by : com.renomad.minum.database.DbEngine2Tests.test_LoadingData_MultipleThreads(com.renomad.minum.database.DbEngine2Tests)
removed call to java/util/stream/Stream::forEach → KILLED

2.2
Location : lambda$walkAndLoad$5
Killed by : com.renomad.minum.database.DbEngine2Tests.test_LoadingData_MultipleThreads(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbEngine2::readAndDeserialize → KILLED

343

1.1
Location : parseConsolidatedFileName
Killed by : com.renomad.minum.database.DbEngine2Tests.test_parseConsolidatedFileName_NegativeCase(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

346

1.1
Location : parseConsolidatedFileName
Killed by : none
replaced long return with 0 for com/renomad/minum/database/DbEngine2::parseConsolidatedFileName → TIMED_OUT

362

1.1
Location : readAndDeserialize
Killed by : com.renomad.minum.database.DbEngine2Tests.test_firstActionIsRequestingDataByIndex(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbEngine2::addToIndexes → KILLED

379

1.1
Location : loadData
Killed by : com.renomad.minum.database.DbEngine2Tests.test_LoadingData_NegativeCase(com.renomad.minum.database.DbEngine2Tests)
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

381

1.1
Location : loadData
Killed by : com.renomad.minum.database.DbEngine2Tests.test_LoadingData_NegativeCase(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

382

1.1
Location : loadData
Killed by : com.renomad.minum.database.DbEngine2Tests.test_LoadingData_NegativeCase(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbEngine2::loadDataFromDisk → KILLED

388

1.1
Location : loadData
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT

407

1.1
Location : values
Killed by : com.renomad.minum.database.DbEngine2Tests.test_ConvertingDatabase_Db_To_DbEngine2(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

2.2
Location : values
Killed by : com.renomad.minum.database.DbEngine2Tests.test_ConvertingDatabase_Db_To_DbEngine2(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbEngine2::loadData → KILLED

409

1.1
Location : values
Killed by : com.renomad.minum.database.DbEngine2Tests.testIndexSpeedDifference(com.renomad.minum.database.DbEngine2Tests)
replaced return value with Collections.emptyList for com/renomad/minum/database/DbEngine2::values → KILLED

414

1.1
Location : registerIndex
Killed by : com.renomad.minum.database.DbEngine2Tests.testIndex_NegativeCase_IndexNameNull(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

418

1.1
Location : registerIndex
Killed by : com.renomad.minum.database.DbEngine2Tests.testIndex_EdgeCase_MultipleIndexes(com.renomad.minum.database.DbEngine2Tests)
replaced boolean return with false for com/renomad/minum/database/DbEngine2::registerIndex → KILLED

2.2
Location : registerIndex
Killed by : none
replaced boolean return with true for com/renomad/minum/database/DbEngine2::registerIndex → SURVIVED
Covering tests

425

1.1
Location : getIndexedData
Killed by : com.renomad.minum.database.DbEngine2Tests.test_firstActionIsRequestingDataByIndex(com.renomad.minum.database.DbEngine2Tests)
negated conditional → KILLED

2.2
Location : getIndexedData
Killed by : com.renomad.minum.database.DbEngine2Tests.test_firstActionIsRequestingDataByIndex(com.renomad.minum.database.DbEngine2Tests)
removed call to com/renomad/minum/database/DbEngine2::loadData → KILLED

426

1.1
Location : getIndexedData
Killed by : com.renomad.minum.database.DbEngine2Tests.testSearchUtility(com.renomad.minum.database.DbEngine2Tests)
replaced return value with Collections.emptyList for com/renomad/minum/database/DbEngine2::getIndexedData → KILLED

437

1.1
Location : flush
Killed by : none
removed call to com/renomad/minum/database/DatabaseAppender::flush → TIMED_OUT

447

1.1
Location : stop
Killed by : none
removed call to com/renomad/minum/database/DbEngine2::flush → TIMED_OUT

456

1.1
Location : stop
Killed by : none
removed call to com/renomad/minum/database/DbEngine2::flush → TIMED_OUT

Active mutators

Tests examined


Report generated by PIT 1.17.0