Skip to content

Commit 9b5a3c8

Browse files
committed
[test] Further tests for large XML nodes which cause overflow pages to be journalled
1 parent 879ef0a commit 9b5a3c8

File tree

9 files changed

+482
-58
lines changed

9 files changed

+482
-58
lines changed

src/org/exist/storage/dom/WriteOverflowPageLoggable.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,12 @@ public void undo() throws LogException {
8787
public String dump() {
8888
return super.dump() + " - writing overflow page " + pageNum + "; next: " + nextPage;
8989
}
90+
91+
public long getPageNum() {
92+
return pageNum;
93+
}
94+
95+
public Value getValue() {
96+
return value;
97+
}
9098
}

src/org/exist/storage/journal/Journal.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,18 @@ public final class Journal {
122122
/**
123123
* default maximum journal size
124124
*/
125-
public static final int DEFAULT_MAX_SIZE = 100; //MB
125+
public static final int DEFAULT_MAX_SIZE = 100; //MB
126126

127127
/**
128128
* minimal size the journal needs to have to be replaced by a new file during a checkpoint
129129
*/
130-
private static final int DEFAULT_MIN_SIZE = 1; // MB
130+
private static final int DEFAULT_MIN_SIZE = 1; // MB
131+
132+
/**
133+
* We use a 1 megabyte buffer.
134+
*/
135+
public static final int BUFFER_SIZE = 1024 * 1024; // bytes
136+
131137

132138
/**
133139
* Minimum size limit for the journal file before it is replaced by a new file.
@@ -220,8 +226,7 @@ public final class Journal {
220226
public Journal(final BrokerPool pool, final Path directory) throws EXistException {
221227
this.pool = pool;
222228
this.fsJournalDir = directory.resolve("fs.journal");
223-
// we use a 1 megabyte buffer:
224-
this.currentBuffer = ByteBuffer.allocateDirect(1024 * 1024);
229+
this.currentBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
225230

226231
this.fileSyncRunnable = new FileSyncRunnable(latch);
227232
this.fileSyncThread = newInstanceThread(pool, "file-sync-thread", fileSyncRunnable);
@@ -296,6 +301,11 @@ public synchronized void writeToLog(final Loggable entry) throws JournalExceptio
296301

297302
SanityCheck.ASSERT(!inRecovery, "Write to log during recovery. Should not happen!");
298303
final int size = entry.getLogSize();
304+
305+
if (size > Short.MAX_VALUE) {
306+
throw new JournalException("Journal can only write log entries of less that 32KB");
307+
}
308+
299309
final int required = size + LOG_ENTRY_BASE_LEN;
300310
if (required > currentBuffer.remaining()) {
301311
flushToLog(false);
@@ -315,7 +325,7 @@ public synchronized void writeToLog(final Loggable entry) throws JournalExceptio
315325
try {
316326
currentBuffer.put(entry.getLogType());
317327
currentBuffer.putLong(entry.getTransactionId());
318-
currentBuffer.putShort((short) entry.getLogSize());
328+
currentBuffer.putShort((short) size);
319329
entry.write(currentBuffer);
320330
currentBuffer.putShort((short) (size + LOG_ENTRY_HEADER_LEN));
321331
} catch (final BufferOverflowException e) {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* eXist Open Source Native XML Database
3+
* Copyright (C) 2001-2018 The eXist Project
4+
* http://exist-db.org
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public License
8+
* as published by the Free Software Foundation; either version 2
9+
* of the License, or (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 Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public
17+
* License along with this library; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*/
20+
21+
package org.exist.util;
22+
23+
import com.evolvedbinary.j8fu.Either;
24+
import org.exist.util.io.FastByteArrayInputStream;
25+
import org.xml.sax.InputSource;
26+
27+
import java.io.*;
28+
29+
import static com.evolvedbinary.j8fu.Either.Left;
30+
import static com.evolvedbinary.j8fu.Either.Right;
31+
32+
public class StringInputSource extends InputSource {
33+
34+
private final Either<byte[], String> source;
35+
36+
/**
37+
* Creates a String Source from a string
38+
* the InputSource will be read using
39+
* {@link #getCharacterStream()}.
40+
*/
41+
public StringInputSource(final String string) {
42+
super();
43+
this.source = Right(string);
44+
}
45+
46+
/**
47+
* Creates a String Source from bytes
48+
* the InputSource will be read using
49+
* {@link #getByteStream()}.
50+
*/
51+
public StringInputSource(final byte[] string) {
52+
super();
53+
this.source = Left(string);
54+
}
55+
56+
@Override
57+
public Reader getCharacterStream() {
58+
if (source.isLeft()) {
59+
return null;
60+
} else {
61+
return new StringReader(source.right().get());
62+
}
63+
}
64+
65+
/**
66+
* @throws IllegalStateException this class is immutable!
67+
*/
68+
@Override
69+
public void setCharacterStream(final Reader r) {
70+
throw new IllegalStateException("StringInputSource is immutable");
71+
}
72+
73+
@Override
74+
public InputStream getByteStream() {
75+
if (source.isLeft()) {
76+
return new FastByteArrayInputStream(source.left().get());
77+
} else {
78+
return null;
79+
}
80+
}
81+
82+
/**
83+
* @throws IllegalStateException this class is immutable!
84+
*/
85+
@Override
86+
public void setByteStream(final InputStream is) {
87+
throw new IllegalStateException("StringInputSource is immutable");
88+
}
89+
}

test/src/org/exist/storage/AbstractRecoverTest.java

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@
3131
import org.exist.test.ExistEmbeddedServer;
3232
import org.exist.test.TestConstants;
3333
import org.exist.util.DatabaseConfigurationException;
34+
import org.exist.util.FileInputSource;
3435
import org.exist.util.FileUtils;
3536
import org.exist.util.LockException;
3637
import org.exist.xmldb.XmldbURI;
3738
import org.junit.Ignore;
3839
import org.junit.Rule;
3940
import org.junit.Test;
41+
import org.xml.sax.InputSource;
4042

4143
import java.io.IOException;
4244
import java.nio.file.Files;
@@ -51,11 +53,11 @@
5153
*/
5254
public abstract class AbstractRecoverTest {
5355

54-
private static final boolean COMMIT = true;
55-
private static final boolean NO_COMMIT = false;
56+
protected static final boolean COMMIT = true;
57+
protected static final boolean NO_COMMIT = false;
5658

57-
private static final boolean MUST_EXIST = true;
58-
private static final boolean MUST_NOT_EXIST = false;
59+
protected static final boolean MUST_EXIST = true;
60+
protected static final boolean MUST_NOT_EXIST = false;
5961

6062
/**
6163
* We set useTemporaryStorage=true for ExistEmbeddedServer
@@ -599,7 +601,7 @@ public void replaceWithoutCommitThenDeleteWithoutCommitAndLoad_isRepeatable() th
599601
* unfinished (i.e. neither committed, aborted, or closed)
600602
* @param file The file that to store
601603
*/
602-
private void store(final boolean commitAndClose, final Path file) throws EXistException, PermissionDeniedException,
604+
protected void store(final boolean commitAndClose, final Path file) throws EXistException, PermissionDeniedException,
603605
IOException, TriggerException, LockException {
604606
store(commitAndClose, file, FileUtils.fileName(file));
605607
}
@@ -614,6 +616,20 @@ private void store(final boolean commitAndClose, final Path file) throws EXistEx
614616
*/
615617
private void store(final boolean commitAndClose, final Path file, final String dbFilename) throws EXistException,
616618
PermissionDeniedException, IOException, TriggerException, LockException {
619+
store(commitAndClose, new FileInputSource(file), dbFilename);
620+
}
621+
622+
/**
623+
* Store a document into the database.
624+
*
625+
* @param commitAndClose true if the transaction should be committed. false will leave the transaction
626+
* unfinished (i.e. neither committed, aborted, or closed)
627+
* @param data The data to store in the document
628+
* @param dbFilename the name to use when storing the file in the database
629+
*/
630+
protected void store(final boolean commitAndClose, final InputSource data, final String dbFilename) throws EXistException,
631+
PermissionDeniedException, IOException, TriggerException, LockException {
632+
617633
final BrokerPool pool = existEmbeddedServer.getBrokerPool();
618634
final TransactionManager transact = pool.getTransactionManager();
619635

@@ -625,7 +641,7 @@ private void store(final boolean commitAndClose, final Path file, final String d
625641
assertNotNull(root);
626642
broker.saveCollection(transaction, root);
627643

628-
storeAndVerify(broker, transaction, root, file, dbFilename);
644+
storeAndVerify(broker, transaction, root, data, dbFilename);
629645

630646
if(commitAndClose) {
631647
transaction.commit();
@@ -640,11 +656,11 @@ private void store(final boolean commitAndClose, final Path file, final String d
640656
* @param broker The database broker
641657
* @param transaction The database transaction
642658
* @param collection The Collection into which the document should be stored
643-
* @param file The file which holds the content for the document to store in the database
659+
* @param data The content for the document to store in the database
644660
* @param dbFilename The name to store the document as in the database
645661
*/
646662
protected abstract void storeAndVerify(final DBBroker broker, final Txn transaction, final Collection collection,
647-
final Path file, final String dbFilename) throws EXistException, PermissionDeniedException,
663+
final InputSource data, final String dbFilename) throws EXistException, PermissionDeniedException,
648664
IOException, TriggerException, LockException;
649665

650666
/**
@@ -667,6 +683,18 @@ private void read(final boolean shouldExist, final Path file)
667683
*/
668684
private void read(final boolean shouldExist, final Path file, final String dbFilename)
669685
throws EXistException, PermissionDeniedException, IOException {
686+
read(shouldExist, new FileInputSource(file), dbFilename);
687+
}
688+
689+
/**
690+
* Read a document from the database.
691+
*
692+
* @param shouldExist true if the document should exist in the database, false if the document should not exist
693+
* @param data The data that was previously stored
694+
* @param dbFilename The name of the file to read from the database
695+
*/
696+
protected void read(final boolean shouldExist, final InputSource data, final String dbFilename)
697+
throws EXistException, PermissionDeniedException, IOException {
670698
final BrokerPool pool = existEmbeddedServer.getBrokerPool();
671699
try(final DBBroker broker = pool.get(Optional.of(pool.getSecurityManager().getSystemSubject()))) {
672700
final XmldbURI uri = TestConstants.TEST_COLLECTION_URI.append(dbFilename);
@@ -677,7 +705,7 @@ private void read(final boolean shouldExist, final Path file, final String dbFil
677705
} else {
678706
assertNotNull("Document does not exist in the database: " + uri, doc);
679707

680-
readAndVerify(broker, doc, file, dbFilename);
708+
readAndVerify(broker, doc, data, dbFilename);
681709
}
682710
}
683711
}
@@ -687,11 +715,11 @@ private void read(final boolean shouldExist, final Path file, final String dbFil
687715
*
688716
* @param broker The database broker.
689717
* @param doc The document from the database.
690-
* @param file The file that was previously stored
718+
* @param data The data that was previously stored
691719
* @param dbFilename The name of the file read from the database
692720
*/
693721
protected abstract void readAndVerify(final DBBroker broker, final DocumentImpl doc,
694-
final Path file, final String dbFilename) throws EXistException, PermissionDeniedException, IOException;
722+
final InputSource data, final String dbFilename) throws EXistException, PermissionDeniedException, IOException;
695723

696724
/**
697725
* Delete a document from the database.
@@ -737,7 +765,7 @@ private void delete(final boolean commitAndClose, final String dbFilename)
737765
}
738766
}
739767

740-
private void flushJournal() {
768+
protected void flushJournal() {
741769
final BrokerPool pool = existEmbeddedServer.getBrokerPool();
742770
pool.getJournalManager().get().flush(true, false);
743771
}

test/src/org/exist/storage/RecoverBinaryTest.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@
3131
import org.exist.dom.persistent.DocumentImpl;
3232
import org.exist.security.PermissionDeniedException;
3333
import org.exist.storage.txn.Txn;
34+
import org.exist.util.FileInputSource;
3435
import org.exist.util.LockException;
3536
import org.exist.xmldb.XmldbURI;
37+
import org.xml.sax.InputSource;
3638

3739
import static org.junit.Assert.assertEquals;
3840
import static org.junit.Assert.assertNotNull;
@@ -54,20 +56,24 @@ protected Path getTestFile2() throws IOException {
5456

5557
@Override
5658
protected void storeAndVerify(final DBBroker broker, final Txn transaction, final Collection collection,
57-
final Path file, final String dbFilename) throws EXistException, PermissionDeniedException, IOException,
59+
final InputSource data, final String dbFilename) throws EXistException, PermissionDeniedException, IOException,
5860
TriggerException, LockException {
5961

60-
final byte[] data = Files.readAllBytes(file);
61-
final BinaryDocument doc = collection.addBinaryResource(transaction, broker, XmldbURI.create(dbFilename), data, "application/octet-stream");
62+
final Path file = ((FileInputSource)data).getFile();
63+
64+
final byte[] content = Files.readAllBytes(file);
65+
final BinaryDocument doc = collection.addBinaryResource(transaction, broker, XmldbURI.create(dbFilename), content, "application/octet-stream");
6266

6367
assertNotNull(doc);
6468
assertEquals(Files.size(file), doc.getContentLength());
6569
}
6670

6771
@Override
68-
protected void readAndVerify(final DBBroker broker, final DocumentImpl doc, final Path file,
72+
protected void readAndVerify(final DBBroker broker, final DocumentImpl doc, final InputSource data,
6973
final String dbFilename) throws IOException {
7074

75+
final Path file = ((FileInputSource)data).getFile();
76+
7177
final BinaryDocument binDoc = (BinaryDocument)doc;
7278

7379
// verify the size, to ensure it is the correct content
@@ -80,8 +86,8 @@ protected void readAndVerify(final DBBroker broker, final DocumentImpl doc, fina
8086
final int read = cis.read(bdata);
8187
assertEquals(bdata.length, read);
8288

83-
final String data = new String(bdata);
84-
assertNotNull(data);
89+
final String content = new String(bdata);
90+
assertNotNull(content);
8591

8692
assertEquals(expectedSize, cis.getByteCount());
8793
}

0 commit comments

Comments
 (0)