From 0ce54d1ddd09eacaa37226b9482eefaea00c8c48 Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Fri, 8 Nov 2019 14:42:29 +0200 Subject: [PATCH 1/8] [core] OfflineDatabase pack API - introduce `OfflineDatabase::pack()` standing for incremental vacuum - make pack optional at offline region deletion - update `OfflineDatabase.DeleteRegion` test accordingly to the items above --- .../include/mbgl/storage/offline_database.hpp | 3 +- .../src/mbgl/storage/offline_database.cpp | 16 +++++-- test/storage/offline_database.test.cpp | 48 ++++++++++++++----- 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/platform/default/include/mbgl/storage/offline_database.hpp b/platform/default/include/mbgl/storage/offline_database.hpp index e599094a6d8..6561d47b226 100644 --- a/platform/default/include/mbgl/storage/offline_database.hpp +++ b/platform/default/include/mbgl/storage/offline_database.hpp @@ -74,7 +74,7 @@ class OfflineDatabase : private util::noncopyable { expected updateMetadata(const int64_t regionID, const OfflineRegionMetadata&); - std::exception_ptr deleteRegion(OfflineRegion&&); + std::exception_ptr deleteRegion(OfflineRegion&&, bool pack = true); std::exception_ptr invalidateRegion(int64_t regionID); // Return value is (response, stored size) @@ -93,6 +93,7 @@ class OfflineDatabase : private util::noncopyable { uint64_t getOfflineMapboxTileCount(); bool exceedsOfflineMapboxTileCountLimit(const Resource&); void markUsedResources(int64_t regionID, const std::list&); + std::exception_ptr pack(); private: void initialize(); diff --git a/platform/default/src/mbgl/storage/offline_database.cpp b/platform/default/src/mbgl/storage/offline_database.cpp index 5aa5738f41f..84a4b1699bd 100644 --- a/platform/default/src/mbgl/storage/offline_database.cpp +++ b/platform/default/src/mbgl/storage/offline_database.cpp @@ -183,6 +183,7 @@ void OfflineDatabase::migrateToVersion6() { } void OfflineDatabase::vacuum() { + assert(db); if (getPragma("PRAGMA auto_vacuum") != 2 /*INCREMENTAL*/) { db->exec("PRAGMA auto_vacuum = INCREMENTAL"); db->exec("VACUUM"); @@ -872,7 +873,7 @@ OfflineDatabase::updateMetadata(const int64_t regionID, const OfflineRegionMetad return unexpected(std::current_exception()); } -std::exception_ptr OfflineDatabase::deleteRegion(OfflineRegion&& region) try { +std::exception_ptr OfflineDatabase::deleteRegion(OfflineRegion&& region, bool pack) try { { mapbox::sqlite::Query query{ getStatement("DELETE FROM regions WHERE id = ?") }; query.bind(1, region.getID()); @@ -881,10 +882,10 @@ std::exception_ptr OfflineDatabase::deleteRegion(OfflineRegion&& region) try { evict(0); assert(db); - vacuum(); + if (pack) vacuum(); // Ensure that the cached offlineTileCount value is recalculated. - offlineMapboxTileCount = {}; + offlineMapboxTileCount = nullopt; return nullptr; } catch (const mapbox::sqlite::Exception& ex) { handleError(ex, "delete region"); @@ -1298,6 +1299,15 @@ void OfflineDatabase::markUsedResources(int64_t regionID, const std::list> generateResources(const std::string& tilePrefix, + const std::string& stylePrefix) { + static const auto responseData = randomString(.5 * 1024 * 1024); + Response response; + response.data = responseData; + std::list> resources; + for (unsigned i = 0; i < 50; ++i) { + resources.emplace_back(Resource::tile(tilePrefix + std::to_string(i), 1, 0, 0, 0, Tileset::Scheme::XYZ), + response); + resources.emplace_back(Resource::style(stylePrefix + std::to_string(i)), response); + } + return resources; +} +} // namespace + TEST(OfflineDatabase, TEST_REQUIRES_WRITE(DeleteRegion)) { FixtureLog log; deleteDatabaseFiles(); @@ -677,25 +693,35 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(DeleteRegion)) { OfflineTilePyramidRegionDefinition definition{ "mapbox://style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0, true }; OfflineRegionMetadata metadata{{ 1, 2, 3 }}; - auto region = db.createRegion(definition, metadata); + auto region1 = db.createRegion(definition, metadata); + auto region2 = db.createRegion(definition, metadata); - for (unsigned i = 0; i < 50; ++i) { - const Resource tile = Resource::tile("mapbox://tile_" + std::to_string(i), 1, 0, 0, 0, Tileset::Scheme::XYZ); - db.putRegionResource(region->getID(), tile, response); + OfflineRegionStatus status; + db.putRegionResources(region1->getID(), generateResources("mapbox://tile_1", "mapbox://style_1"), status); + db.putRegionResources(region2->getID(), generateResources("mapbox://tile_2", "mapbox://style_2"), status); + const size_t sizeWithTwoRegions = util::read_file(filename).size(); - const Resource style = Resource::style("mapbox://style_" + std::to_string(i)); - db.putRegionResource(region->getID(), style, response); - } + db.deleteRegion(std::move(*region1), false /*pack*/); - db.deleteRegion(std::move(*region)); + ASSERT_EQ(1u, db.listRegions().value().size()); + // Region is removed but the size of the database is the same. + EXPECT_EQ(sizeWithTwoRegions, util::read_file(filename).size()); - auto regions = db.listRegions().value(); - ASSERT_EQ(0u, regions.size()); + db.pack(); + // The size of the database has shrunk after pack(). + const size_t sizeWithOneRegion = util::read_file(filename).size(); + EXPECT_LT(sizeWithOneRegion, sizeWithTwoRegions); + + db.deleteRegion(std::move(*region2)); + // The size of the database has shrunk right away. + const size_t sizeWithoutRegions = util::read_file(filename).size(); + ASSERT_EQ(0u, db.listRegions().value().size()); + EXPECT_LT(sizeWithoutRegions, sizeWithOneRegion); // The tiles from the offline region will migrate to the // ambient cache and shrink the database to the maximum // size defined by default. - EXPECT_LE(util::read_file(filename).size(), util::DEFAULT_MAX_CACHE_SIZE); + EXPECT_LE(sizeWithoutRegions, util::DEFAULT_MAX_CACHE_SIZE); // After clearing the cache, the size of the database // should get back to the original size. From a739db86ff4769317993974be03d2fd1c0efa445 Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Fri, 8 Nov 2019 15:47:29 +0200 Subject: [PATCH 2/8] [core] Extend DefaultFileSource API Add `packDatabase()` method and `bool pack` argument to the `deleteOfflineRegion()` method. --- include/mbgl/storage/default_file_source.hpp | 20 +++++++++++++++--- .../src/mbgl/storage/default_file_source.cpp | 21 +++++++++++++------ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp index 4f4c7136c6a..8fc8ed50df1 100644 --- a/include/mbgl/storage/default_file_source.hpp +++ b/include/mbgl/storage/default_file_source.hpp @@ -121,6 +121,11 @@ class DefaultFileSource : public FileSource { * Eviction works by removing the least-recently requested resources not also required * by other regions, until the database shrinks below a certain size. * + * If the |pack| argument is `false` the database file packing is skipped and the + * database file does not shrink after this operation completes. Database file + * packing can be done later with `packDatabase()`. It is a useful optimization + * e.g. when several regions should be deleted in a row. + * * Note that this method takes ownership of the input, reflecting the fact that once * region deletion is initiated, it is not legal to perform further actions with the * region. @@ -129,7 +134,7 @@ class DefaultFileSource : public FileSource { * executed on the database thread; it is the responsibility of the SDK bindings * to re-execute a user-provided callback on the main thread. */ - void deleteOfflineRegion(OfflineRegion&&, std::function); + void deleteOfflineRegion(OfflineRegion&&, std::function, bool pack = true); /* * Invalidate all the tiles from an offline region forcing Mapbox GL to revalidate @@ -137,7 +142,7 @@ class DefaultFileSource : public FileSource { * offline region and downloading it again because if the data on the cache matches * the server, no new data gets transmitted. */ - void invalidateOfflineRegion(OfflineRegion&, std::function); + void invalidateOfflineRegion(OfflineRegion&, std::function); /* * Changing or bypassing this limit without permission from Mapbox is prohibited @@ -179,7 +184,16 @@ class DefaultFileSource : public FileSource { * executed on the database thread; it is the responsibility of the SDK bindings * to re-execute a user-provided callback on the main thread. */ - void resetDatabase(std::function); + void resetDatabase(std::function); + + /* + * Packs the existing database file into a minimal amount of disk space. + * + * When the operation is complete or encounters an error, the given callback will be + * executed on the database thread; it is the responsibility of the SDK bindings + * to re-execute a user-provided callback on the main thread. + */ + void packDatabase(std::function callback); /* * Forces revalidation of the ambient cache. diff --git a/platform/default/src/mbgl/storage/default_file_source.cpp b/platform/default/src/mbgl/storage/default_file_source.cpp index dc4b2908a8c..3982f3dbc77 100644 --- a/platform/default/src/mbgl/storage/default_file_source.cpp +++ b/platform/default/src/mbgl/storage/default_file_source.cpp @@ -82,9 +82,9 @@ class DefaultFileSource::Impl { } } - void deleteRegion(OfflineRegion&& region, std::function callback) { + void deleteRegion(OfflineRegion&& region, std::function callback, bool pack) { downloads.erase(region.getID()); - callback(offlineDatabase->deleteRegion(std::move(region))); + callback(offlineDatabase->deleteRegion(std::move(region), pack)); } void invalidateRegion(int64_t regionID, std::function callback) { @@ -200,6 +200,8 @@ class DefaultFileSource::Impl { callback(offlineDatabase->setMaximumAmbientCacheSize(size)); } + void packDatabase(std::function callback) { callback(offlineDatabase->pack()); } + private: expected getDownload(int64_t regionID) { auto it = downloads.find(regionID); @@ -308,11 +310,14 @@ void DefaultFileSource::updateOfflineMetadata(const int64_t regionID, impl->actor().invoke(&Impl::updateMetadata, regionID, metadata, callback); } -void DefaultFileSource::deleteOfflineRegion(OfflineRegion&& region, std::function callback) { - impl->actor().invoke(&Impl::deleteRegion, std::move(region), callback); +void DefaultFileSource::deleteOfflineRegion(OfflineRegion&& region, + std::function callback, + bool pack) { + impl->actor().invoke(&Impl::deleteRegion, std::move(region), callback, pack); } -void DefaultFileSource::invalidateOfflineRegion(OfflineRegion& region, std::function callback) { +void DefaultFileSource::invalidateOfflineRegion(OfflineRegion& region, + std::function callback) { impl->actor().invoke(&Impl::invalidateRegion, region.getID(), callback); } @@ -348,11 +353,15 @@ void DefaultFileSource::resetDatabase(std::function c impl->actor().invoke(&Impl::resetDatabase, std::move(callback)); } +void DefaultFileSource::packDatabase(std::function callback) { + impl->actor().invoke(&Impl::packDatabase, std::move(callback)); +} + void DefaultFileSource::invalidateAmbientCache(std::function callback) { impl->actor().invoke(&Impl::invalidateAmbientCache, std::move(callback)); } -void DefaultFileSource::clearAmbientCache(std::function callback) { +void DefaultFileSource::clearAmbientCache(std::function callback) { impl->actor().invoke(&Impl::clearAmbientCache, std::move(callback)); } From 1659743e3de0011bcf6617a5470191d578b1ff25 Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Fri, 8 Nov 2019 17:18:37 +0200 Subject: [PATCH 3/8] [android] Add OfflineManager.packDatabase() API --- .../mapboxsdk/offline/OfflineManager.java | 44 +++++++++++++++++++ .../mapboxsdk/testapp/offline/CacheTest.kt | 16 +++++++ .../android/src/offline/offline_manager.cpp | 27 +++++++++++- .../android/src/offline/offline_manager.hpp | 2 + 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java index faed46662ab..0051c17e030 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java @@ -306,6 +306,47 @@ public void run() { }); } + /** + * Packs the existing database file into a minimal amount of disk space. + *

+ * When the operation is complete or encounters an error, the given callback will be + * executed on the database thread; it is the responsibility of the SDK bindings + * to re-execute a user-provided callback on the main thread. + *

+ * + * @param callback the callback to be invoked when the database was reset or when the operation erred. + */ + public void packDatabase(@Nullable final FileSourceCallback callback) { + fileSource.activate(); + nativePackDatabase(new FileSourceCallback() { + @Override + public void onSuccess() { + handler.post(new Runnable() { + @Override + public void run() { + fileSource.deactivate(); + if (callback != null) { + callback.onSuccess(); + } + } + }); + } + + @Override + public void onError(@NonNull final String message) { + handler.post(new Runnable() { + @Override + public void run() { + fileSource.deactivate(); + if (callback != null) { + callback.onError(message); + } + } + }); + } + }); + } + /** * Forces re-validation of the ambient cache. *

@@ -648,6 +689,9 @@ private native void createOfflineRegion(FileSource fileSource, OfflineRegionDefi @Keep private native void nativeResetDatabase(@Nullable FileSourceCallback callback); + @Keep + private native void nativePackDatabase(@Nullable FileSourceCallback callback); + @Keep private native void nativeInvalidateAmbientCache(@Nullable FileSourceCallback callback); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/CacheTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/CacheTest.kt index a3214c9f114..da4cfbaf018 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/CacheTest.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/CacheTest.kt @@ -85,4 +85,20 @@ class CacheTest { } countDownLatch.await() } + + @Test + fun testSetPackDatabase() { + rule.activity.runOnUiThread { + OfflineManager.getInstance(context).packDatabase(object : OfflineManager.FileSourceCallback { + override fun onSuccess() { + countDownLatch.countDown() + } + + override fun onError(message: String) { + Assert.assertNull("onError should not be called", message) + } + }) + } + countDownLatch.await() + } } \ No newline at end of file diff --git a/platform/android/src/offline/offline_manager.cpp b/platform/android/src/offline/offline_manager.cpp index 029252f7867..8d76f685221 100644 --- a/platform/android/src/offline/offline_manager.cpp +++ b/platform/android/src/offline/offline_manager.cpp @@ -8,7 +8,6 @@ namespace mbgl { namespace android { // OfflineManager // - OfflineManager::OfflineManager(jni::JNIEnv& env, const jni::Object& jFileSource) : fileSource(std::static_pointer_cast(mbgl::FileSource::getSharedFileSource(FileSource::getSharedResourceOptions(env, jFileSource)))) {} @@ -123,6 +122,26 @@ void OfflineManager::resetDatabase(jni::JNIEnv& env_, const jni::Object& callback_) { + auto globalCallback = jni::NewGlobal(env_, callback_); + + fileSource->packDatabase( + [ + // Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile + callback = std::make_shared(std::move(globalCallback))]( + std::exception_ptr exception) mutable { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (exception) { + OfflineManager::FileSourceCallback::onError( + *env, *callback, jni::Make(*env, mbgl::util::toString(exception))); + } else { + OfflineManager::FileSourceCallback::onSuccess(*env, *callback); + } + }); +} + void OfflineManager::invalidateAmbientCache(jni::JNIEnv& env_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); @@ -207,7 +226,10 @@ void OfflineManager::registerNative(jni::JNIEnv& env) { #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) - jni::RegisterNativePeer( env, javaClass, "nativePtr", + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", jni::MakePeer&>, "initialize", "finalize", @@ -216,6 +238,7 @@ void OfflineManager::registerNative(jni::JNIEnv& env) { METHOD(&OfflineManager::createOfflineRegion, "createOfflineRegion"), METHOD(&OfflineManager::mergeOfflineRegions, "mergeOfflineRegions"), METHOD(&OfflineManager::resetDatabase, "nativeResetDatabase"), + METHOD(&OfflineManager::packDatabase, "nativePackDatabase"), METHOD(&OfflineManager::invalidateAmbientCache, "nativeInvalidateAmbientCache"), METHOD(&OfflineManager::clearAmbientCache, "nativeClearAmbientCache"), METHOD(&OfflineManager::setMaximumAmbientCacheSize, "nativeSetMaximumAmbientCacheSize"), diff --git a/platform/android/src/offline/offline_manager.hpp b/platform/android/src/offline/offline_manager.hpp index 058cfb5b484..64e00f91fc2 100644 --- a/platform/android/src/offline/offline_manager.hpp +++ b/platform/android/src/offline/offline_manager.hpp @@ -95,6 +95,8 @@ class OfflineManager { void resetDatabase(jni::JNIEnv&, const jni::Object& callback_); + void packDatabase(jni::JNIEnv&, const jni::Object& callback_); + void invalidateAmbientCache(jni::JNIEnv&, const jni::Object& callback_); void clearAmbientCache(jni::JNIEnv&, const jni::Object& callback_); From 7c87d7220c78c166d7aea222fd8d100d814811cd Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Fri, 8 Nov 2019 17:58:52 +0200 Subject: [PATCH 4/8] [android] Consolidate exception handling code in OfflineManager --- .../android/src/offline/offline_manager.cpp | 103 ++++++------------ 1 file changed, 36 insertions(+), 67 deletions(-) diff --git a/platform/android/src/offline/offline_manager.cpp b/platform/android/src/offline/offline_manager.cpp index 8d76f685221..c45386e3a78 100644 --- a/platform/android/src/offline/offline_manager.cpp +++ b/platform/android/src/offline/offline_manager.cpp @@ -7,6 +7,20 @@ namespace mbgl { namespace android { +namespace { +// Reattach, the callback comes from a different thread +void handleException(std::exception_ptr exception, + const jni::Object& callback, + android::UniqueEnv env = android::AttachEnv()) { + if (exception) { + OfflineManager::FileSourceCallback::onError( + *env, callback, jni::Make(*env, mbgl::util::toString(exception))); + } else { + OfflineManager::FileSourceCallback::onSuccess(*env, callback); + } +} +} // namespace + // OfflineManager // OfflineManager::OfflineManager(jni::JNIEnv& env, const jni::Object& jFileSource) : fileSource(std::static_pointer_cast(mbgl::FileSource::getSharedFileSource(FileSource::getSharedResourceOptions(env, jFileSource)))) {} @@ -106,20 +120,11 @@ void OfflineManager::mergeOfflineRegions(jni::JNIEnv& env_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); - fileSource->resetDatabase([ - //Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile - callback = std::make_shared(std::move(globalCallback)) - ](std::exception_ptr exception) mutable { - - // Reattach, the callback comes from a different thread - android::UniqueEnv env = android::AttachEnv(); - - if (exception) { - OfflineManager::FileSourceCallback::onError(*env, *callback, jni::Make(*env, mbgl::util::toString(exception))); - } else { - OfflineManager::FileSourceCallback::onSuccess(*env, *callback); - } - }); + fileSource->resetDatabase( + [ + // Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile + callback = std::make_shared(std::move(globalCallback))]( + std::exception_ptr exception) mutable { handleException(exception, *callback); }); } void OfflineManager::packDatabase(jni::JNIEnv& env_, const jni::Object& callback_) { @@ -129,74 +134,38 @@ void OfflineManager::packDatabase(jni::JNIEnv& env_, const jni::Object(std::move(globalCallback))]( - std::exception_ptr exception) mutable { - // Reattach, the callback comes from a different thread - android::UniqueEnv env = android::AttachEnv(); - - if (exception) { - OfflineManager::FileSourceCallback::onError( - *env, *callback, jni::Make(*env, mbgl::util::toString(exception))); - } else { - OfflineManager::FileSourceCallback::onSuccess(*env, *callback); - } - }); + std::exception_ptr exception) mutable { handleException(exception, *callback); }); } void OfflineManager::invalidateAmbientCache(jni::JNIEnv& env_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); - fileSource->invalidateAmbientCache([ - //Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile - callback = std::make_shared(std::move(globalCallback)) - ](std::exception_ptr exception) mutable { - - // Reattach, the callback comes from a different thread - android::UniqueEnv env = android::AttachEnv(); - - if (exception) { - OfflineManager::FileSourceCallback::onError(*env, *callback, jni::Make(*env, mbgl::util::toString(exception))); - } else { - OfflineManager::FileSourceCallback::onSuccess(*env, *callback); - } - }); + fileSource->invalidateAmbientCache( + [ + // Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile + callback = std::make_shared(std::move(globalCallback))]( + std::exception_ptr exception) mutable { handleException(exception, *callback); }); } void OfflineManager::clearAmbientCache(jni::JNIEnv& env_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); - fileSource->clearAmbientCache([ - //Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile - callback = std::make_shared(std::move(globalCallback)) - ](std::exception_ptr exception) mutable { - - // Reattach, the callback comes from a different thread - android::UniqueEnv env = android::AttachEnv(); - - if (exception) { - OfflineManager::FileSourceCallback::onError(*env, *callback, jni::Make(*env, mbgl::util::toString(exception))); - } else { - OfflineManager::FileSourceCallback::onSuccess(*env, *callback); - } - }); + fileSource->clearAmbientCache( + [ + // Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile + callback = std::make_shared(std::move(globalCallback))]( + std::exception_ptr exception) mutable { handleException(exception, *callback); }); } void OfflineManager::setMaximumAmbientCacheSize(jni::JNIEnv& env_, const jni::jlong size_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); - fileSource->setMaximumAmbientCacheSize(size_, [ - //Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile - callback = std::make_shared(std::move(globalCallback)) - ](std::exception_ptr exception) mutable { - - // Reattach, the callback comes from a different thread - android::UniqueEnv env = android::AttachEnv(); - - if (exception) { - OfflineManager::FileSourceCallback::onError(*env, *callback, jni::Make(*env, mbgl::util::toString(exception))); - } else { - OfflineManager::FileSourceCallback::onSuccess(*env, *callback); - } - }); + fileSource->setMaximumAmbientCacheSize( + size_, + [ + // Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile + callback = std::make_shared(std::move(globalCallback))]( + std::exception_ptr exception) mutable { handleException(exception, *callback); }); } // FileSource::FileSourceCallback // From a3f4aa0065441c1497b11d6adcacc0d08000a8fb Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Mon, 25 Nov 2019 12:18:32 +0200 Subject: [PATCH 5/8] [core] Introduce OfflineDatabase::runPackDatabaseAutomatically() API - added a unit test - Updated inline comments in default_file_source.hpp --- include/mbgl/storage/default_file_source.hpp | 30 ++++++--- .../android/src/offline/offline_region.cpp | 67 ++++++++++--------- .../include/mbgl/storage/offline_database.hpp | 4 +- .../src/mbgl/storage/default_file_source.cpp | 16 +++-- .../src/mbgl/storage/offline_database.cpp | 10 +-- test/storage/offline_database.test.cpp | 5 +- 6 files changed, 78 insertions(+), 54 deletions(-) diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp index 8fc8ed50df1..0a06934a3f0 100644 --- a/include/mbgl/storage/default_file_source.hpp +++ b/include/mbgl/storage/default_file_source.hpp @@ -121,20 +121,18 @@ class DefaultFileSource : public FileSource { * Eviction works by removing the least-recently requested resources not also required * by other regions, until the database shrinks below a certain size. * - * If the |pack| argument is `false` the database file packing is skipped and the - * database file does not shrink after this operation completes. Database file - * packing can be done later with `packDatabase()`. It is a useful optimization - * e.g. when several regions should be deleted in a row. - * * Note that this method takes ownership of the input, reflecting the fact that once * region deletion is initiated, it is not legal to perform further actions with the * region. * + * Note that this operation can be potentially slow if packing the database occurs + * automatically (see runPackDatabaseAutomatically() and packDatabase()). + * * When the operation is complete or encounters an error, the given callback will be * executed on the database thread; it is the responsibility of the SDK bindings * to re-execute a user-provided callback on the main thread. */ - void deleteOfflineRegion(OfflineRegion&&, std::function, bool pack = true); + void deleteOfflineRegion(OfflineRegion&&, std::function); /* * Invalidate all the tiles from an offline region forcing Mapbox GL to revalidate @@ -189,12 +187,25 @@ class DefaultFileSource : public FileSource { /* * Packs the existing database file into a minimal amount of disk space. * + * This operation has a performance impact as it will vacuum the database, + * forcing it to move pages on the filesystem. + * * When the operation is complete or encounters an error, the given callback will be * executed on the database thread; it is the responsibility of the SDK bindings * to re-execute a user-provided callback on the main thread. */ void packDatabase(std::function callback); + /* + * Sets whether packing the database file occurs automatically after an offline + * region is deleted (deleteOfflineRegion()) or the ambient cache is cleared + * (clearAmbientCache()). + * + * By default, packing is enabled. If disabled, disk space will not be freed + * after resources are removed unless packDatabase() is explicitly called. + */ + void runPackDatabaseAutomatically(bool); + /* * Forces revalidation of the ambient cache. * @@ -212,9 +223,10 @@ class DefaultFileSource : public FileSource { /* * Erase resources from the ambient cache, freeing storage space. * - * Erases the ambient cache, freeing resources. This operation can be - * potentially slow because it will trigger a VACUUM on SQLite, - * forcing the database to move pages on the filesystem. + * Erases the ambient cache, freeing resources. + * + * Note that this operation can be potentially slow if packing the database + * occurs automatically (see runPackDatabaseAutomatically() and packDatabase()). * * Resources overlapping with offline regions will not be affected * by this call. diff --git a/platform/android/src/offline/offline_region.cpp b/platform/android/src/offline/offline_region.cpp index ac9f491ab6f..4276ce8f45f 100644 --- a/platform/android/src/offline/offline_region.cpp +++ b/platform/android/src/offline/offline_region.cpp @@ -104,37 +104,40 @@ void OfflineRegion::getOfflineRegionStatus(jni::JNIEnv& env_, const jni::Object< void OfflineRegion::deleteOfflineRegion(jni::JNIEnv& env_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); - fileSource->deleteOfflineRegion(std::move(*region), [ - //Ensure the object is not gc'd in the meanwhile - callback = std::make_shared(std::move(globalCallback)) - ](std::exception_ptr error) mutable { - // Reattach, the callback comes from a different thread - android::UniqueEnv env = android::AttachEnv(); - - if (error) { - OfflineRegionDeleteCallback::onError(*env, *callback, error); - } else { - OfflineRegionDeleteCallback::onDelete(*env, *callback); - } - }); + fileSource->deleteOfflineRegion(std::move(*region), + [ + // Ensure the object is not gc'd in the meanwhile + callback = std::make_shared( + std::move(globalCallback))](std::exception_ptr error) mutable { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (error) { + OfflineRegionDeleteCallback::onError(*env, *callback, error); + } else { + OfflineRegionDeleteCallback::onDelete(*env, *callback); + } + }); } -void OfflineRegion::invalidateOfflineRegion(jni::JNIEnv& env_, const jni::Object& callback_) { +void OfflineRegion::invalidateOfflineRegion(jni::JNIEnv& env_, + const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); - fileSource->invalidateOfflineRegion(*region, [ - //Ensure the object is not gc'd in the meanwhile - callback = std::make_shared(std::move(globalCallback)) - ](std::exception_ptr error) mutable { - // Reattach, the callback comes from a different thread - android::UniqueEnv env = android::AttachEnv(); - - if (error) { - OfflineRegionInvalidateCallback::onError(*env, *callback, error); - } else { - OfflineRegionInvalidateCallback::onInvalidate(*env, *callback); - } - }); + fileSource->invalidateOfflineRegion(*region, + [ + // Ensure the object is not gc'd in the meanwhile + callback = std::make_shared( + std::move(globalCallback))](std::exception_ptr error) mutable { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (error) { + OfflineRegionInvalidateCallback::onError(*env, *callback, error); + } else { + OfflineRegionInvalidateCallback::onInvalidate(*env, *callback); + } + }); } void OfflineRegion::updateOfflineRegionMetadata(jni::JNIEnv& env_, const jni::Array& jMetadata, const jni::Object& callback_) { @@ -206,7 +209,10 @@ void OfflineRegion::registerNative(jni::JNIEnv& env) { #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) - jni::RegisterNativePeer( env, javaClass, "nativePtr", + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", jni::MakePeer&>, "initialize", "finalize", @@ -215,8 +221,7 @@ void OfflineRegion::registerNative(jni::JNIEnv& env) { METHOD(&OfflineRegion::getOfflineRegionStatus, "getOfflineRegionStatus"), METHOD(&OfflineRegion::deleteOfflineRegion, "deleteOfflineRegion"), METHOD(&OfflineRegion::invalidateOfflineRegion, "invalidateOfflineRegion"), - METHOD(&OfflineRegion::updateOfflineRegionMetadata, "updateOfflineRegionMetadata") - ); + METHOD(&OfflineRegion::updateOfflineRegionMetadata, "updateOfflineRegionMetadata")); } // OfflineRegionObserver // @@ -227,7 +232,7 @@ void OfflineRegion::OfflineRegionStatusCallback::onError(jni::JNIEnv& env, const jni::Object& callback, std::exception_ptr error) { static auto& javaClass = jni::Class::Singleton(env); - static auto method = javaClass.GetMethod(env, "onError"); + static auto method = javaClass.GetMethod(env, "onError"); callback.Call(env, method, jni::Make(env, mbgl::util::toString(error))); } diff --git a/platform/default/include/mbgl/storage/offline_database.hpp b/platform/default/include/mbgl/storage/offline_database.hpp index 6561d47b226..465b4dd20bf 100644 --- a/platform/default/include/mbgl/storage/offline_database.hpp +++ b/platform/default/include/mbgl/storage/offline_database.hpp @@ -74,7 +74,7 @@ class OfflineDatabase : private util::noncopyable { expected updateMetadata(const int64_t regionID, const OfflineRegionMetadata&); - std::exception_ptr deleteRegion(OfflineRegion&&, bool pack = true); + std::exception_ptr deleteRegion(OfflineRegion&&); std::exception_ptr invalidateRegion(int64_t regionID); // Return value is (response, stored size) @@ -94,6 +94,7 @@ class OfflineDatabase : private util::noncopyable { bool exceedsOfflineMapboxTileCountLimit(const Resource&); void markUsedResources(int64_t regionID, const std::list&); std::exception_ptr pack(); + void runPackDatabaseAutomatically(bool autopack_) { autopack = autopack_; } private: void initialize(); @@ -148,6 +149,7 @@ class OfflineDatabase : private util::noncopyable { optional offlineMapboxTileCount; bool evict(uint64_t neededFreeSize); + bool autopack = true; }; } // namespace mbgl diff --git a/platform/default/src/mbgl/storage/default_file_source.cpp b/platform/default/src/mbgl/storage/default_file_source.cpp index 3982f3dbc77..82073109e4e 100644 --- a/platform/default/src/mbgl/storage/default_file_source.cpp +++ b/platform/default/src/mbgl/storage/default_file_source.cpp @@ -82,9 +82,9 @@ class DefaultFileSource::Impl { } } - void deleteRegion(OfflineRegion&& region, std::function callback, bool pack) { + void deleteRegion(OfflineRegion&& region, std::function callback) { downloads.erase(region.getID()); - callback(offlineDatabase->deleteRegion(std::move(region), pack)); + callback(offlineDatabase->deleteRegion(std::move(region))); } void invalidateRegion(int64_t regionID, std::function callback) { @@ -202,6 +202,8 @@ class DefaultFileSource::Impl { void packDatabase(std::function callback) { callback(offlineDatabase->pack()); } + void runPackDatabaseAutomatically(bool autopack) { offlineDatabase->runPackDatabaseAutomatically(autopack); } + private: expected getDownload(int64_t regionID) { auto it = downloads.find(regionID); @@ -310,10 +312,8 @@ void DefaultFileSource::updateOfflineMetadata(const int64_t regionID, impl->actor().invoke(&Impl::updateMetadata, regionID, metadata, callback); } -void DefaultFileSource::deleteOfflineRegion(OfflineRegion&& region, - std::function callback, - bool pack) { - impl->actor().invoke(&Impl::deleteRegion, std::move(region), callback, pack); +void DefaultFileSource::deleteOfflineRegion(OfflineRegion&& region, std::function callback) { + impl->actor().invoke(&Impl::deleteRegion, std::move(region), callback); } void DefaultFileSource::invalidateOfflineRegion(OfflineRegion& region, @@ -357,6 +357,10 @@ void DefaultFileSource::packDatabase(std::function cal impl->actor().invoke(&Impl::packDatabase, std::move(callback)); } +void DefaultFileSource::runPackDatabaseAutomatically(bool autopack) { + impl->actor().invoke(&Impl::runPackDatabaseAutomatically, autopack); +} + void DefaultFileSource::invalidateAmbientCache(std::function callback) { impl->actor().invoke(&Impl::invalidateAmbientCache, std::move(callback)); } diff --git a/platform/default/src/mbgl/storage/offline_database.cpp b/platform/default/src/mbgl/storage/offline_database.cpp index 84a4b1699bd..3051f095498 100644 --- a/platform/default/src/mbgl/storage/offline_database.cpp +++ b/platform/default/src/mbgl/storage/offline_database.cpp @@ -140,7 +140,7 @@ void OfflineDatabase::removeExisting() { void OfflineDatabase::removeOldCacheTable() { assert(db); db->exec("DROP TABLE IF EXISTS http_cache"); - vacuum(); + if (autopack) vacuum(); } void OfflineDatabase::createSchema() { @@ -694,7 +694,7 @@ std::exception_ptr OfflineDatabase::clearAmbientCache() try { resourceQuery.run(); - vacuum(); + if (autopack) vacuum(); return nullptr; } catch (const mapbox::sqlite::Exception& ex) { @@ -873,7 +873,7 @@ OfflineDatabase::updateMetadata(const int64_t regionID, const OfflineRegionMetad return unexpected(std::current_exception()); } -std::exception_ptr OfflineDatabase::deleteRegion(OfflineRegion&& region, bool pack) try { +std::exception_ptr OfflineDatabase::deleteRegion(OfflineRegion&& region) try { { mapbox::sqlite::Query query{ getStatement("DELETE FROM regions WHERE id = ?") }; query.bind(1, region.getID()); @@ -882,7 +882,7 @@ std::exception_ptr OfflineDatabase::deleteRegion(OfflineRegion&& region, bool pa evict(0); assert(db); - if (pack) vacuum(); + if (autopack) vacuum(); // Ensure that the cached offlineTileCount value is recalculated. offlineMapboxTileCount = nullopt; @@ -1229,7 +1229,7 @@ std::exception_ptr OfflineDatabase::setMaximumAmbientCacheSize(uint64_t size) { if (databaseSize > maximumAmbientCacheSize) { evict(0); - vacuum(); + if (autopack) vacuum(); } return nullptr; diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp index 9d798d0a627..e9164e7c667 100644 --- a/test/storage/offline_database.test.cpp +++ b/test/storage/offline_database.test.cpp @@ -701,7 +701,8 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(DeleteRegion)) { db.putRegionResources(region2->getID(), generateResources("mapbox://tile_2", "mapbox://style_2"), status); const size_t sizeWithTwoRegions = util::read_file(filename).size(); - db.deleteRegion(std::move(*region1), false /*pack*/); + db.runPackDatabaseAutomatically(false); + db.deleteRegion(std::move(*region1)); ASSERT_EQ(1u, db.listRegions().value().size()); // Region is removed but the size of the database is the same. @@ -711,7 +712,7 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(DeleteRegion)) { // The size of the database has shrunk after pack(). const size_t sizeWithOneRegion = util::read_file(filename).size(); EXPECT_LT(sizeWithOneRegion, sizeWithTwoRegions); - + db.runPackDatabaseAutomatically(true); db.deleteRegion(std::move(*region2)); // The size of the database has shrunk right away. const size_t sizeWithoutRegions = util::read_file(filename).size(); From 92b70683baaba988126fa4231a8e9b70db8ca5cb Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Mon, 25 Nov 2019 14:44:46 +0200 Subject: [PATCH 6/8] [core] Add OfflineDatabase.Pack unit test --- include/mbgl/storage/default_file_source.hpp | 6 ++-- test/storage/offline_database.test.cpp | 30 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp index 0a06934a3f0..4937524e372 100644 --- a/include/mbgl/storage/default_file_source.hpp +++ b/include/mbgl/storage/default_file_source.hpp @@ -200,7 +200,7 @@ class DefaultFileSource : public FileSource { * Sets whether packing the database file occurs automatically after an offline * region is deleted (deleteOfflineRegion()) or the ambient cache is cleared * (clearAmbientCache()). - * + * * By default, packing is enabled. If disabled, disk space will not be freed * after resources are removed unless packDatabase() is explicitly called. */ @@ -223,8 +223,8 @@ class DefaultFileSource : public FileSource { /* * Erase resources from the ambient cache, freeing storage space. * - * Erases the ambient cache, freeing resources. - * + * Erases the ambient cache, freeing resources. + * * Note that this operation can be potentially slow if packing the database * occurs automatically (see runPackDatabaseAutomatically() and packDatabase()). * diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp index e9164e7c667..ef7ad257d74 100644 --- a/test/storage/offline_database.test.cpp +++ b/test/storage/offline_database.test.cpp @@ -733,6 +733,36 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(DeleteRegion)) { EXPECT_EQ(0u, log.uncheckedCount()); } +TEST(OfflineDatabase, TEST_REQUIRES_WRITE(Pack)) { + FixtureLog log; + deleteDatabaseFiles(); + + OfflineDatabase db(filename); + size_t initialSize = util::read_file(filename).size(); + db.runPackDatabaseAutomatically(false); + + Response response; + response.data = randomString(.5 * 1024 * 1024); + + for (unsigned i = 0; i < 50; ++i) { + const Resource tile = Resource::tile("mapbox://tile_" + std::to_string(i), 1, 0, 0, 0, Tileset::Scheme::XYZ); + db.put(tile, response); + + const Resource style = Resource::style("mapbox://style_" + std::to_string(i)); + db.put(style, response); + } + size_t populatedSize = util::read_file(filename).size(); + ASSERT_GT(populatedSize, initialSize); + + db.clearAmbientCache(); + EXPECT_EQ(populatedSize, util::read_file(filename).size()); + EXPECT_EQ(0u, log.uncheckedCount()); + + db.pack(); + EXPECT_EQ(initialSize, util::read_file(filename).size()); + EXPECT_EQ(0u, log.uncheckedCount()); +} + TEST(OfflineDatabase, MapboxTileLimitExceeded) { FixtureLog log; From cc97ad3b2578edaa3fcf732d1c05179ee055b25d Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Mon, 25 Nov 2019 15:17:37 +0200 Subject: [PATCH 7/8] [android] Add OfflineManager.runPackDatabaseAutomatically(boolean) API --- .../mapboxsdk/offline/OfflineManager.java | 30 ++++++++++++++++--- .../mapboxsdk/offline/OfflineRegion.java | 4 +++ .../android/src/offline/offline_manager.cpp | 5 ++++ .../android/src/offline/offline_manager.hpp | 2 ++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java index 0051c17e030..a7bb9a99850 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java @@ -309,6 +309,9 @@ public void run() { /** * Packs the existing database file into a minimal amount of disk space. *

+ * This operation has a performance impact as it will vacuum the database, + * forcing it to move pages on the filesystem. + *

* When the operation is complete or encounters an error, the given callback will be * executed on the database thread; it is the responsibility of the SDK bindings * to re-execute a user-provided callback on the main thread. @@ -396,9 +399,10 @@ public void run() { /** * Erase resources from the ambient cache, freeing storage space. *

- * Erases the ambient cache, freeing resources. This operation can be - * potentially slow because it will trigger a VACUUM on SQLite, - * forcing the database to move pages on the filesystem. + * Erases the ambient cache, freeing resources. + *

+ * Note that this operation can be potentially slow if packing the database + * occurs automatically ({@link OfflineManager#runPackDatabaseAutomatically(boolean)}). *

*

* Resources overlapping with offline regions will not be affected @@ -661,7 +665,7 @@ private boolean isValidOfflineRegionDefinition(OfflineRegionDefinition definitio *

* Once this limit is reached, {@link OfflineRegion.OfflineRegionObserver#mapboxTileCountLimitExceeded(long)} * fires every additional attempt to download additional tiles until already downloaded tiles are removed - * by calling {@link OfflineRegion#deleteOfflineRegion(OfflineRegion.OfflineRegionDeleteCallback)}. + * by calling {@link OfflineRegion#delete(OfflineRegion.OfflineRegionDeleteCallback)}. *

* * @param limit the maximum number of tiles allowed to be downloaded @@ -669,6 +673,24 @@ private boolean isValidOfflineRegionDefinition(OfflineRegionDefinition definitio @Keep public native void setOfflineMapboxTileCountLimit(long limit); + /** + * Sets whether database file packing occurs automatically. + * By default, the automatic database file packing is enabled. + *

+ * If packing is enabled, database file packing occurs automatically + * after an offline region is deleted by calling + * {@link OfflineRegion#delete(OfflineRegion.OfflineRegionDeleteCallback)} + * or the ambient cache is cleared by calling {@link OfflineManager#clearAmbientCache()}. + * + * If packing is disabled, disk space will not be freed after + * resources are removed unless {@link OfflineManager#packDatabase()} is explicitly called. + *

+ * + * @param autopack flag setting the automatic database file packing. + */ + @Keep + public native void runPackDatabaseAutomatically(boolean autopack); + @Keep private native void initialize(FileSource fileSource); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java index f13128da632..d116535c5a6 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java @@ -388,6 +388,10 @@ public void run() { * by other regions, until the database shrinks below a certain size. *

*

+ * Note that this operation can be potentially slow if packing the database + * occurs automatically ({@link OfflineManager#runPackDatabaseAutomatically(boolean)}). + *

+ *

* When the operation is complete or encounters an error, the given callback will be * executed on the main thread. *

diff --git a/platform/android/src/offline/offline_manager.cpp b/platform/android/src/offline/offline_manager.cpp index c45386e3a78..be864e18aa7 100644 --- a/platform/android/src/offline/offline_manager.cpp +++ b/platform/android/src/offline/offline_manager.cpp @@ -168,6 +168,10 @@ void OfflineManager::setMaximumAmbientCacheSize(jni::JNIEnv& env_, const jni::jl std::exception_ptr exception) mutable { handleException(exception, *callback); }); } +void OfflineManager::runPackDatabaseAutomatically(jni::JNIEnv&, jboolean autopack) { + fileSource->runPackDatabaseAutomatically(autopack); +} + // FileSource::FileSourceCallback // void OfflineManager::FileSourceCallback::onSuccess(jni::JNIEnv& env, @@ -211,6 +215,7 @@ void OfflineManager::registerNative(jni::JNIEnv& env) { METHOD(&OfflineManager::invalidateAmbientCache, "nativeInvalidateAmbientCache"), METHOD(&OfflineManager::clearAmbientCache, "nativeClearAmbientCache"), METHOD(&OfflineManager::setMaximumAmbientCacheSize, "nativeSetMaximumAmbientCacheSize"), + METHOD(&OfflineManager::runPackDatabaseAutomatically, "runPackDatabaseAutomatically"), METHOD(&OfflineManager::putResourceWithUrl, "putResourceWithUrl")); } diff --git a/platform/android/src/offline/offline_manager.hpp b/platform/android/src/offline/offline_manager.hpp index 64e00f91fc2..0af92f81158 100644 --- a/platform/android/src/offline/offline_manager.hpp +++ b/platform/android/src/offline/offline_manager.hpp @@ -103,6 +103,8 @@ class OfflineManager { void setMaximumAmbientCacheSize(jni::JNIEnv&, const jni::jlong size, const jni::Object& callback_); + void runPackDatabaseAutomatically(jni::JNIEnv&, jboolean autopack); + private: std::shared_ptr fileSource; }; From d4e57252f15497ea0d3f723d66cf0bf6824262d8 Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Mon, 9 Dec 2019 14:05:29 +0200 Subject: [PATCH 8/8] [android] Add change log entry --- platform/android/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 2631a4f4234..cdca5ecfc5f 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -11,6 +11,8 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to ### Performance improvements - Enable incremental vacuum for the offline database in order to make data removal requests faster and to avoid the excessive disk space usage (creating a backup file on VACUUM call) [#15837](https://github.com/mapbox/mapbox-gl-native/pull/15837) + - Introduce `OfflineManager.packDatabase()` API, which explicitly packs the database file (i.e. runs incremental vacuum for the offline database) [#15899](https://github.com/mapbox/mapbox-gl-native/pull/15899) + - Introduce `OfflineManager.runPackDatabaseAutomatically(boolean)`, which sets whether database file packing (i.e. incremental vacuum operation for the offline database) occurs automatically. [#15967](https://github.com/mapbox/mapbox-gl-native/pull/15967) ## 8.5.0-alpha.2 - October 10, 2019 [Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.5.0-alpha.1...android-v8.5.0-alpha.2) since [Mapbox Maps SDK for Android v8.5.0-alpha.1](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.5.0-alpha.1):