Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 47847f3f authored by Jeff Brown's avatar Jeff Brown
Browse files

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
parent 3c4da3ca
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -5204,6 +5204,7 @@ package android.content {
    field public static final java.lang.String LAYOUT_INFLATER_SERVICE = "layout_inflater";
    field public static final java.lang.String LOCATION_SERVICE = "location";
    field public static final int MODE_APPEND = 32768; // 0x8000
    field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
    field public static final int MODE_MULTI_PROCESS = 4; // 0x4
    field public static final int MODE_PRIVATE = 0; // 0x0
    field public static final int MODE_WORLD_READABLE = 1; // 0x1
@@ -7334,6 +7335,7 @@ package android.database.sqlite {
    method public deprecated boolean isDbLockedByOtherThreads();
    method public boolean isOpen();
    method public boolean isReadOnly();
    method public boolean isWriteAheadLoggingEnabled();
    method public deprecated void markTableSyncable(java.lang.String, java.lang.String);
    method public deprecated void markTableSyncable(java.lang.String, java.lang.String, java.lang.String);
    method public boolean needUpgrade(int);
@@ -7375,6 +7377,7 @@ package android.database.sqlite {
    field public static final int CONFLICT_REPLACE = 5; // 0x5
    field public static final int CONFLICT_ROLLBACK = 1; // 0x1
    field public static final int CREATE_IF_NECESSARY = 268435456; // 0x10000000
    field public static final int ENABLE_WRITE_AHEAD_LOGGING = 536870912; // 0x20000000
    field public static final int MAX_SQL_CACHE_SIZE = 100; // 0x64
    field public static final int NO_LOCALIZED_COLLATORS = 16; // 0x10
    field public static final int OPEN_READONLY = 1; // 0x1
@@ -7438,6 +7441,7 @@ package android.database.sqlite {
    method public void onDowngrade(android.database.sqlite.SQLiteDatabase, int, int);
    method public void onOpen(android.database.sqlite.SQLiteDatabase);
    method public abstract void onUpgrade(android.database.sqlite.SQLiteDatabase, int, int);
    method public void setWriteAheadLoggingEnabled(boolean);
  }
  public class SQLiteOutOfMemoryException extends android.database.sqlite.SQLiteException {
+6 −5
Original line number Diff line number Diff line
@@ -766,17 +766,18 @@ class ContextImpl extends Context {

    @Override
    public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
        File f = validateFilePath(name, true);
        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f, factory);
        setFilePermissionsFromMode(f.getPath(), mode, 0);
        return db;
        return openOrCreateDatabase(name, mode, factory, null);
    }

    @Override
    public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory,
            DatabaseErrorHandler errorHandler) {
        File f = validateFilePath(name, true);
        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f.getPath(), factory, errorHandler);
        int flags = SQLiteDatabase.CREATE_IF_NECESSARY;
        if ((mode & MODE_ENABLE_WRITE_AHEAD_LOGGING) != 0) {
            flags |= SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING;
        }
        SQLiteDatabase db = SQLiteDatabase.openDatabase(f.getPath(), factory, flags, errorHandler);
        setFilePermissionsFromMode(f.getPath(), mode, 0);
        return db;
    }
+14 −0
Original line number Diff line number Diff line
@@ -98,6 +98,16 @@ public abstract class Context {
     */
    public static final int MODE_MULTI_PROCESS = 0x0004;

    /**
     * Database open flag: when set, the database is opened with write-ahead
     * logging enabled by default.
     *
     * @see #openOrCreateDatabase(String, int, CursorFactory)
     * @see #openOrCreateDatabase(String, int, CursorFactory, DatabaseErrorHandler)
     * @see SQLiteDatabase#enableWriteAheadLogging
     */
    public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 0x0008;

    /**
     * Flag for {@link #bindService}: automatically create the service as long
     * as the binding exists.  Note that while this will create the service,
@@ -691,6 +701,7 @@ public abstract class Context {
     * @param mode Operating mode.  Use 0 or {@link #MODE_PRIVATE} for the
     *     default operation, {@link #MODE_WORLD_READABLE}
     *     and {@link #MODE_WORLD_WRITEABLE} to control permissions.
     *     Use {@link #MODE_ENABLE_WRITE_AHEAD_LOGGING} to enable write-ahead logging by default.
     * @param factory An optional factory class that is called to instantiate a
     *     cursor when query is called.
     *
@@ -700,6 +711,7 @@ public abstract class Context {
     * @see #MODE_PRIVATE
     * @see #MODE_WORLD_READABLE
     * @see #MODE_WORLD_WRITEABLE
     * @see #MODE_ENABLE_WRITE_AHEAD_LOGGING
     * @see #deleteDatabase
     */
    public abstract SQLiteDatabase openOrCreateDatabase(String name,
@@ -716,6 +728,7 @@ public abstract class Context {
     * @param mode Operating mode.  Use 0 or {@link #MODE_PRIVATE} for the
     *     default operation, {@link #MODE_WORLD_READABLE}
     *     and {@link #MODE_WORLD_WRITEABLE} to control permissions.
     *     Use {@link #MODE_ENABLE_WRITE_AHEAD_LOGGING} to enable write-ahead logging by default.
     * @param factory An optional factory class that is called to instantiate a
     *     cursor when query is called.
     * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
@@ -726,6 +739,7 @@ public abstract class Context {
     * @see #MODE_PRIVATE
     * @see #MODE_WORLD_READABLE
     * @see #MODE_WORLD_WRITEABLE
     * @see #MODE_ENABLE_WRITE_AHEAD_LOGGING
     * @see #deleteDatabase
     */
    public abstract SQLiteDatabase openOrCreateDatabase(String name,
+3 −2
Original line number Diff line number Diff line
@@ -269,7 +269,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen

    private void setWalModeFromConfiguration() {
        if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
            if (mConfiguration.walEnabled) {
            if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
                setJournalMode("WAL");
                setSyncMode(SQLiteGlobal.getWALSyncMode());
            } else {
@@ -389,7 +389,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
        }

        // Remember what changed.
        boolean walModeChanged = configuration.walEnabled != mConfiguration.walEnabled;
        boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
                & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
        boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);

        // Update configuration parameters.
+24 −10
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ public final class SQLiteConnectionPool implements Closeable {
    private final Object mLock = new Object();
    private final AtomicBoolean mConnectionLeaked = new AtomicBoolean();
    private final SQLiteDatabaseConfiguration mConfiguration;
    private int mMaxConnectionPoolSize;
    private boolean mIsOpen;
    private int mNextConnectionId;

@@ -146,6 +147,7 @@ public final class SQLiteConnectionPool implements Closeable {

    private SQLiteConnectionPool(SQLiteDatabaseConfiguration configuration) {
        mConfiguration = new SQLiteDatabaseConfiguration(configuration);
        setMaxConnectionPoolSizeLocked();
    }

    @Override
@@ -257,8 +259,9 @@ public final class SQLiteConnectionPool implements Closeable {
        synchronized (mLock) {
            throwIfClosedLocked();

            boolean restrictToOneConnection = false;
            if (mConfiguration.walEnabled != configuration.walEnabled) {
            boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
                    & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
            if (walModeChanged) {
                // WAL mode can only be changed if there are no acquired connections
                // because we need to close all but the primary connection first.
                if (!mAcquiredConnections.isEmpty()) {
@@ -272,15 +275,13 @@ public final class SQLiteConnectionPool implements Closeable {
                // because none of them are in use.
                closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
                assert mAvailableNonPrimaryConnections.isEmpty();

                restrictToOneConnection = true;
            }

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

@@ -296,9 +297,11 @@ public final class SQLiteConnectionPool implements Closeable {

                mAvailablePrimaryConnection = newPrimaryConnection;
                mConfiguration.updateParametersFrom(configuration);
                setMaxConnectionPoolSizeLocked();
            } else {
                // Reconfigure the database connections in place.
                mConfiguration.updateParametersFrom(configuration);
                setMaxConnectionPoolSizeLocked();

                closeExcessConnectionsAndLogExceptionsLocked();
                reconfigureAllConnectionsLocked();
@@ -360,8 +363,7 @@ public final class SQLiteConnectionPool implements Closeable {
                    mAvailablePrimaryConnection = connection;
                }
                wakeConnectionWaitersLocked();
            } else if (mAvailableNonPrimaryConnections.size() >=
                    mConfiguration.maxConnectionPoolSize - 1) {
            } else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize - 1) {
                closeConnectionAndLogExceptionsLocked(connection);
            } else {
                if (recycleConnectionLocked(connection, status)) {
@@ -499,7 +501,7 @@ public final class SQLiteConnectionPool implements Closeable {
    // Can't throw.
    private void closeExcessConnectionsAndLogExceptionsLocked() {
        int availableCount = mAvailableNonPrimaryConnections.size();
        while (availableCount-- > mConfiguration.maxConnectionPoolSize - 1) {
        while (availableCount-- > mMaxConnectionPoolSize - 1) {
            SQLiteConnection connection =
                    mAvailableNonPrimaryConnections.remove(availableCount);
            closeConnectionAndLogExceptionsLocked(connection);
@@ -874,7 +876,7 @@ public final class SQLiteConnectionPool implements Closeable {
        if (mAvailablePrimaryConnection != null) {
            openConnections += 1;
        }
        if (openConnections >= mConfiguration.maxConnectionPoolSize) {
        if (openConnections >= mMaxConnectionPoolSize) {
            return null;
        }
        connection = openConnectionLocked(mConfiguration,
@@ -926,6 +928,18 @@ public final class SQLiteConnectionPool implements Closeable {
        return (connectionFlags & CONNECTION_FLAG_INTERACTIVE) != 0 ? 1 : 0;
    }

    private void setMaxConnectionPoolSizeLocked() {
        if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
            mMaxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
        } else {
            // TODO: We don't actually need to restrict the connection pool size to 1
            // for non-WAL databases.  There might be reasons to use connection pooling
            // with other journal modes.  For now, enabling connection pooling and
            // using WAL are the same thing in the API.
            mMaxConnectionPoolSize = 1;
        }
    }

    private void throwIfClosedLocked() {
        if (!mIsOpen) {
            throw new IllegalStateException("Cannot perform this operation "
@@ -972,7 +986,7 @@ public final class SQLiteConnectionPool implements Closeable {
        synchronized (mLock) {
            printer.println("Connection pool for " + mConfiguration.path + ":");
            printer.println("  Open: " + mIsOpen);
            printer.println("  Max connections: " + mConfiguration.maxConnectionPoolSize);
            printer.println("  Max connections: " + mMaxConnectionPoolSize);

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