Skip to content

Commit 47847f3

Browse files
author
Jeff Brown
committed
Support enabling WAL using a flag when DB is opened.
Using enableWriteAheadLogging() to enable WAL is inefficient because we previously disabled WAL mode when the database was opened. Switching from WAL to PERSIST then back to WAL is inefficient and could slow down application launch time. It would be better to leave the database in WAL mode when we open it to begin with. To do that, we need to know ahead of time whether we will want to have WAL enabled for the newly opened database. Using this flag also reduces the chance that we will encounter an error enabling WAL mode due to there being other open connections to the database. Bug: 6124556 Change-Id: I38ec7a528baeda9f1ef77e25e88b3ca4b6296200
1 parent 3c4da3c commit 47847f3

File tree

10 files changed

+182
-88
lines changed

10 files changed

+182
-88
lines changed

api/current.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5204,6 +5204,7 @@ package android.content {
52045204
field public static final java.lang.String LAYOUT_INFLATER_SERVICE = "layout_inflater";
52055205
field public static final java.lang.String LOCATION_SERVICE = "location";
52065206
field public static final int MODE_APPEND = 32768; // 0x8000
5207+
field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
52075208
field public static final int MODE_MULTI_PROCESS = 4; // 0x4
52085209
field public static final int MODE_PRIVATE = 0; // 0x0
52095210
field public static final int MODE_WORLD_READABLE = 1; // 0x1
@@ -7334,6 +7335,7 @@ package android.database.sqlite {
73347335
method public deprecated boolean isDbLockedByOtherThreads();
73357336
method public boolean isOpen();
73367337
method public boolean isReadOnly();
7338+
method public boolean isWriteAheadLoggingEnabled();
73377339
method public deprecated void markTableSyncable(java.lang.String, java.lang.String);
73387340
method public deprecated void markTableSyncable(java.lang.String, java.lang.String, java.lang.String);
73397341
method public boolean needUpgrade(int);
@@ -7375,6 +7377,7 @@ package android.database.sqlite {
73757377
field public static final int CONFLICT_REPLACE = 5; // 0x5
73767378
field public static final int CONFLICT_ROLLBACK = 1; // 0x1
73777379
field public static final int CREATE_IF_NECESSARY = 268435456; // 0x10000000
7380+
field public static final int ENABLE_WRITE_AHEAD_LOGGING = 536870912; // 0x20000000
73787381
field public static final int MAX_SQL_CACHE_SIZE = 100; // 0x64
73797382
field public static final int NO_LOCALIZED_COLLATORS = 16; // 0x10
73807383
field public static final int OPEN_READONLY = 1; // 0x1
@@ -7438,6 +7441,7 @@ package android.database.sqlite {
74387441
method public void onDowngrade(android.database.sqlite.SQLiteDatabase, int, int);
74397442
method public void onOpen(android.database.sqlite.SQLiteDatabase);
74407443
method public abstract void onUpgrade(android.database.sqlite.SQLiteDatabase, int, int);
7444+
method public void setWriteAheadLoggingEnabled(boolean);
74417445
}
74427446

74437447
public class SQLiteOutOfMemoryException extends android.database.sqlite.SQLiteException {

core/java/android/app/ContextImpl.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -766,17 +766,18 @@ public String[] fileList() {
766766

767767
@Override
768768
public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
769-
File f = validateFilePath(name, true);
770-
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f, factory);
771-
setFilePermissionsFromMode(f.getPath(), mode, 0);
772-
return db;
769+
return openOrCreateDatabase(name, mode, factory, null);
773770
}
774771

775772
@Override
776773
public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory,
777774
DatabaseErrorHandler errorHandler) {
778775
File f = validateFilePath(name, true);
779-
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f.getPath(), factory, errorHandler);
776+
int flags = SQLiteDatabase.CREATE_IF_NECESSARY;
777+
if ((mode & MODE_ENABLE_WRITE_AHEAD_LOGGING) != 0) {
778+
flags |= SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING;
779+
}
780+
SQLiteDatabase db = SQLiteDatabase.openDatabase(f.getPath(), factory, flags, errorHandler);
780781
setFilePermissionsFromMode(f.getPath(), mode, 0);
781782
return db;
782783
}

core/java/android/content/Context.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ public abstract class Context {
9898
*/
9999
public static final int MODE_MULTI_PROCESS = 0x0004;
100100

101+
/**
102+
* Database open flag: when set, the database is opened with write-ahead
103+
* logging enabled by default.
104+
*
105+
* @see #openOrCreateDatabase(String, int, CursorFactory)
106+
* @see #openOrCreateDatabase(String, int, CursorFactory, DatabaseErrorHandler)
107+
* @see SQLiteDatabase#enableWriteAheadLogging
108+
*/
109+
public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 0x0008;
110+
101111
/**
102112
* Flag for {@link #bindService}: automatically create the service as long
103113
* as the binding exists. Note that while this will create the service,
@@ -691,6 +701,7 @@ public abstract FileOutputStream openFileOutput(String name, int mode)
691701
* @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the
692702
* default operation, {@link #MODE_WORLD_READABLE}
693703
* and {@link #MODE_WORLD_WRITEABLE} to control permissions.
704+
* Use {@link #MODE_ENABLE_WRITE_AHEAD_LOGGING} to enable write-ahead logging by default.
694705
* @param factory An optional factory class that is called to instantiate a
695706
* cursor when query is called.
696707
*
@@ -700,6 +711,7 @@ public abstract FileOutputStream openFileOutput(String name, int mode)
700711
* @see #MODE_PRIVATE
701712
* @see #MODE_WORLD_READABLE
702713
* @see #MODE_WORLD_WRITEABLE
714+
* @see #MODE_ENABLE_WRITE_AHEAD_LOGGING
703715
* @see #deleteDatabase
704716
*/
705717
public abstract SQLiteDatabase openOrCreateDatabase(String name,
@@ -716,6 +728,7 @@ public abstract SQLiteDatabase openOrCreateDatabase(String name,
716728
* @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the
717729
* default operation, {@link #MODE_WORLD_READABLE}
718730
* and {@link #MODE_WORLD_WRITEABLE} to control permissions.
731+
* Use {@link #MODE_ENABLE_WRITE_AHEAD_LOGGING} to enable write-ahead logging by default.
719732
* @param factory An optional factory class that is called to instantiate a
720733
* cursor when query is called.
721734
* @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
@@ -726,6 +739,7 @@ public abstract SQLiteDatabase openOrCreateDatabase(String name,
726739
* @see #MODE_PRIVATE
727740
* @see #MODE_WORLD_READABLE
728741
* @see #MODE_WORLD_WRITEABLE
742+
* @see #MODE_ENABLE_WRITE_AHEAD_LOGGING
729743
* @see #deleteDatabase
730744
*/
731745
public abstract SQLiteDatabase openOrCreateDatabase(String name,

core/java/android/database/sqlite/SQLiteConnection.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ private void setJournalSizeLimit() {
269269

270270
private void setWalModeFromConfiguration() {
271271
if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
272-
if (mConfiguration.walEnabled) {
272+
if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
273273
setJournalMode("WAL");
274274
setSyncMode(SQLiteGlobal.getWALSyncMode());
275275
} else {
@@ -389,7 +389,8 @@ void reconfigure(SQLiteDatabaseConfiguration configuration) {
389389
}
390390

391391
// Remember what changed.
392-
boolean walModeChanged = configuration.walEnabled != mConfiguration.walEnabled;
392+
boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
393+
& SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
393394
boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
394395

395396
// Update configuration parameters.

core/java/android/database/sqlite/SQLiteConnectionPool.java

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public final class SQLiteConnectionPool implements Closeable {
8181
private final Object mLock = new Object();
8282
private final AtomicBoolean mConnectionLeaked = new AtomicBoolean();
8383
private final SQLiteDatabaseConfiguration mConfiguration;
84+
private int mMaxConnectionPoolSize;
8485
private boolean mIsOpen;
8586
private int mNextConnectionId;
8687

@@ -146,6 +147,7 @@ enum AcquiredConnectionStatus {
146147

147148
private SQLiteConnectionPool(SQLiteDatabaseConfiguration configuration) {
148149
mConfiguration = new SQLiteDatabaseConfiguration(configuration);
150+
setMaxConnectionPoolSizeLocked();
149151
}
150152

151153
@Override
@@ -257,8 +259,9 @@ public void reconfigure(SQLiteDatabaseConfiguration configuration) {
257259
synchronized (mLock) {
258260
throwIfClosedLocked();
259261

260-
boolean restrictToOneConnection = false;
261-
if (mConfiguration.walEnabled != configuration.walEnabled) {
262+
boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
263+
& SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
264+
if (walModeChanged) {
262265
// WAL mode can only be changed if there are no acquired connections
263266
// because we need to close all but the primary connection first.
264267
if (!mAcquiredConnections.isEmpty()) {
@@ -272,15 +275,13 @@ public void reconfigure(SQLiteDatabaseConfiguration configuration) {
272275
// because none of them are in use.
273276
closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
274277
assert mAvailableNonPrimaryConnections.isEmpty();
275-
276-
restrictToOneConnection = true;
277278
}
278279

279280
if (mConfiguration.openFlags != configuration.openFlags) {
280281
// If we are changing open flags and WAL mode at the same time, then
281282
// we have no choice but to close the primary connection beforehand
282283
// because there can only be one connection open when we change WAL mode.
283-
if (restrictToOneConnection) {
284+
if (walModeChanged) {
284285
closeAvailableConnectionsAndLogExceptionsLocked();
285286
}
286287

@@ -296,9 +297,11 @@ public void reconfigure(SQLiteDatabaseConfiguration configuration) {
296297

297298
mAvailablePrimaryConnection = newPrimaryConnection;
298299
mConfiguration.updateParametersFrom(configuration);
300+
setMaxConnectionPoolSizeLocked();
299301
} else {
300302
// Reconfigure the database connections in place.
301303
mConfiguration.updateParametersFrom(configuration);
304+
setMaxConnectionPoolSizeLocked();
302305

303306
closeExcessConnectionsAndLogExceptionsLocked();
304307
reconfigureAllConnectionsLocked();
@@ -360,8 +363,7 @@ public void releaseConnection(SQLiteConnection connection) {
360363
mAvailablePrimaryConnection = connection;
361364
}
362365
wakeConnectionWaitersLocked();
363-
} else if (mAvailableNonPrimaryConnections.size() >=
364-
mConfiguration.maxConnectionPoolSize - 1) {
366+
} else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize - 1) {
365367
closeConnectionAndLogExceptionsLocked(connection);
366368
} else {
367369
if (recycleConnectionLocked(connection, status)) {
@@ -499,7 +501,7 @@ private void closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked() {
499501
// Can't throw.
500502
private void closeExcessConnectionsAndLogExceptionsLocked() {
501503
int availableCount = mAvailableNonPrimaryConnections.size();
502-
while (availableCount-- > mConfiguration.maxConnectionPoolSize - 1) {
504+
while (availableCount-- > mMaxConnectionPoolSize - 1) {
503505
SQLiteConnection connection =
504506
mAvailableNonPrimaryConnections.remove(availableCount);
505507
closeConnectionAndLogExceptionsLocked(connection);
@@ -874,7 +876,7 @@ private SQLiteConnection tryAcquireNonPrimaryConnectionLocked(
874876
if (mAvailablePrimaryConnection != null) {
875877
openConnections += 1;
876878
}
877-
if (openConnections >= mConfiguration.maxConnectionPoolSize) {
879+
if (openConnections >= mMaxConnectionPoolSize) {
878880
return null;
879881
}
880882
connection = openConnectionLocked(mConfiguration,
@@ -926,6 +928,18 @@ private static int getPriority(int connectionFlags) {
926928
return (connectionFlags & CONNECTION_FLAG_INTERACTIVE) != 0 ? 1 : 0;
927929
}
928930

931+
private void setMaxConnectionPoolSizeLocked() {
932+
if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
933+
mMaxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
934+
} else {
935+
// TODO: We don't actually need to restrict the connection pool size to 1
936+
// for non-WAL databases. There might be reasons to use connection pooling
937+
// with other journal modes. For now, enabling connection pooling and
938+
// using WAL are the same thing in the API.
939+
mMaxConnectionPoolSize = 1;
940+
}
941+
}
942+
929943
private void throwIfClosedLocked() {
930944
if (!mIsOpen) {
931945
throw new IllegalStateException("Cannot perform this operation "
@@ -972,7 +986,7 @@ public void dump(Printer printer, boolean verbose) {
972986
synchronized (mLock) {
973987
printer.println("Connection pool for " + mConfiguration.path + ":");
974988
printer.println(" Open: " + mIsOpen);
975-
printer.println(" Max connections: " + mConfiguration.maxConnectionPoolSize);
989+
printer.println(" Max connections: " + mMaxConnectionPoolSize);
976990

977991
printer.println(" Available primary connection:");
978992
if (mAvailablePrimaryConnection != null) {

0 commit comments

Comments
 (0)