Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[core] Add style::Source::setVolatile()/isVolatile() API #16422

Merged
merged 8 commits into from
Apr 22, 2020
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@

Currently, the `distance` expression supports `Point`, `MultiPoint`, `LineString`, `MultiLineString` geometry types.

- [core] Introduce style::Source::setVolatile()/isVolatile() API ([#16422](https://github.com/mapbox/mapbox-gl-native/pull/16422))

The `Source::setVolatile(bool)` method sets a flag defining whether or not the fetched tiles for the given source should be stored in the local cache.

The corresponding `Source::isVolatile()` getter is added too.

By default, the source is not volatile.

## maps-v1.6.0-rc.1

### ✨ New features
Expand Down
3 changes: 3 additions & 0 deletions include/mbgl/storage/resource.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class Resource {
Offline
};

enum class StoragePolicy : bool { Permanent, Volatile };

struct TileData {
std::string urlTemplate;
uint8_t pixelRatio;
Expand Down Expand Up @@ -96,6 +98,7 @@ class Resource {
optional<std::string> priorEtag = {};
std::shared_ptr<const std::string> priorData;
Duration minimumUpdateInterval{Duration::zero()};
StoragePolicy storagePolicy{StoragePolicy::Permanent};
};

inline bool Resource::hasLoadingMethod(Resource::LoadingMethod method) const {
Expand Down
12 changes: 8 additions & 4 deletions include/mbgl/style/source.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,14 @@ class Source {
std::string getID() const;
optional<std::string> getAttribution() const;

// The data from the volatile sources are not stored in a persistent storage.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pozdnyakov thanks for the quick turnaround on this. Implementation looks great. Do we know how likely we are to introduce additional storage options for sources in the future?

From an API extensibility perspective, I'm wondering if it would be better to introduce smth like Source::getStoragePolicy and Source::setStoragePolicy. If we were to introduce new storage types alongside volatile and permanent in the future, we wouldn't need to create new APIs and the boolean flags wouldn't need to be deprecated.

cc @tmpsantos @alexshalamov @LukasPaczos @tobrun @1ec5 @julianrex

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chloekraw the Carbon API for sources uses generic parameters, so changing from a boolean flag to a enumeration type in future shall be painless 🙂

bool isVolatile() const noexcept;
void setVolatile(bool) noexcept;

// Private implementation
class Impl;
Immutable<Impl> baseImpl;

Source(Immutable<Impl>);

void setObserver(SourceObserver*);
SourceObserver* observer = nullptr;

Expand All @@ -80,8 +82,9 @@ class Source {
optional<uint8_t> getPrefetchZoomDelta() const noexcept;

// If the given source supports loading tiles from a server,
// sets the minimum tile update interval, which is used to
// throttle the tile update network requests.
// sets the minimum tile update interval.
// Update network requests that are more frequent than the
// minimum tile update interval are suppressed.
//
// Default value is `Duration::zero()`.
void setMinimumTileUpdateInterval(Duration) noexcept;
Expand Down Expand Up @@ -114,6 +117,7 @@ class Source {
virtual mapbox::base::WeakPtr<Source> makeWeakPtr() = 0;

protected:
explicit Source(Immutable<Impl>);
virtual Mutable<Impl> createMutable() const noexcept = 0;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class DatabaseFileSourceThread {
: db(std::make_unique<OfflineDatabase>(cachePath)), onlineFileSource(std::move(onlineFileSource_)) {}

void request(const Resource& resource, const ActorRef<FileSourceRequest>& req) {
auto offlineResponse = db->get(resource);
optional<Response> offlineResponse =
(resource.storagePolicy != Resource::StoragePolicy::Volatile) ? db->get(resource) : nullopt;
if (!offlineResponse) {
offlineResponse.emplace();
offlineResponse->noContent = true;
Expand Down Expand Up @@ -179,6 +180,8 @@ std::unique_ptr<AsyncRequest> DatabaseFileSource::request(const Resource& resour
}

void DatabaseFileSource::forward(const Resource& res, const Response& response, std::function<void()> callback) {
if (res.storagePolicy == Resource::StoragePolicy::Volatile) return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also have something like this when requesting volatile resources from the offline database. If they are never stored there is no point in querying for them.


std::function<void()> wrapper;
if (callback) {
wrapper = Scheduler::GetCurrent()->bindOnce(std::move(callback));
Expand Down
3 changes: 2 additions & 1 deletion src/mbgl/renderer/tile_pyramid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
const optional<uint8_t>& sourcePrefetchZoomDelta = sourceImpl.getPrefetchZoomDelta();
const optional<uint8_t>& maxParentTileOverscaleFactor = sourceImpl.getMaxOverscaleFactorForParentTiles();
const Duration minimumUpdateInterval = sourceImpl.getMinimumTileUpdateInterval();
const bool isVolatile = sourceImpl.isVolatile();

std::vector<OverscaledTileID> idealTiles;
std::vector<OverscaledTileID> panTiles;
Expand Down Expand Up @@ -132,7 +133,7 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l

auto retainTileFn = [&](Tile& tile, TileNecessity necessity) -> void {
if (retain.emplace(tile.id).second) {
tile.setMinimumUpdateInterval(minimumUpdateInterval);
tile.setUpdateParameters({minimumUpdateInterval, isVolatile});
tile.setNecessity(necessity);
}

Expand Down
12 changes: 12 additions & 0 deletions src/mbgl/style/source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ optional<std::string> Source::getAttribution() const {
return baseImpl->getAttribution();
}

bool Source::isVolatile() const noexcept {
return baseImpl->isVolatile();
}

void Source::setVolatile(bool set) noexcept {
if (isVolatile() == set) return;
auto newImpl = createMutable();
newImpl->setVolatile(set);
baseImpl = std::move(newImpl);
observer->onSourceChanged(*this);
}

void Source::setObserver(SourceObserver* observer_) {
observer = observer_ ? observer_ : &nullObserver;
}
Expand Down
4 changes: 3 additions & 1 deletion src/mbgl/style/source_impl.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once

#include <mbgl/style/source.hpp>
#include <mbgl/util/noncopyable.hpp>

#include <string>

Expand All @@ -27,13 +26,16 @@ class Source::Impl {
void setMaxOverscaleFactorForParentTiles(optional<uint8_t> overscaleFactor) noexcept;
optional<uint8_t> getMaxOverscaleFactorForParentTiles() const noexcept;

bool isVolatile() const { return volatileFlag; }
void setVolatile(bool set) { volatileFlag = set; }
const SourceType type;
const std::string id;

protected:
optional<uint8_t> prefetchZoomDelta;
optional<uint8_t> maxOverscaleFactor;
Duration minimumTileUpdateInterval{Duration::zero()};
bool volatileFlag = false;

Impl(SourceType, std::string);
Impl(const Impl&) = default;
Expand Down
4 changes: 2 additions & 2 deletions src/mbgl/tile/raster_dem_tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ void RasterDEMTile::setNecessity(TileNecessity necessity) {
loader.setNecessity(necessity);
}

void RasterDEMTile::setMinimumUpdateInterval(Duration interval) {
loader.setMinimumUpdateInterval(interval);
void RasterDEMTile::setUpdateParameters(const TileUpdateParameters& params) {
loader.setUpdateParameters(params);
}

} // namespace mbgl
2 changes: 1 addition & 1 deletion src/mbgl/tile/raster_dem_tile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class RasterDEMTile final : public Tile {

std::unique_ptr<TileRenderData> createRenderData() override;
void setNecessity(TileNecessity) override;
void setMinimumUpdateInterval(Duration) override;
void setUpdateParameters(const TileUpdateParameters&) override;

void setError(std::exception_ptr);
void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires);
Expand Down
4 changes: 2 additions & 2 deletions src/mbgl/tile/raster_tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ void RasterTile::setNecessity(TileNecessity necessity) {
loader.setNecessity(necessity);
}

void RasterTile::setMinimumUpdateInterval(Duration interval) {
loader.setMinimumUpdateInterval(interval);
void RasterTile::setUpdateParameters(const TileUpdateParameters& params) {
loader.setUpdateParameters(params);
}

} // namespace mbgl
2 changes: 1 addition & 1 deletion src/mbgl/tile/raster_tile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class RasterTile final : public Tile {

std::unique_ptr<TileRenderData> createRenderData() override;
void setNecessity(TileNecessity) override;
void setMinimumUpdateInterval(Duration) override;
void setUpdateParameters(const TileUpdateParameters&) override;

void setError(std::exception_ptr);
void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires);
Expand Down
15 changes: 14 additions & 1 deletion src/mbgl/tile/tile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ namespace gfx {
class UploadPass;
} // namespace gfx

struct TileUpdateParameters {
Duration minimumUpdateInterval;
bool isVolatile;
};

inline bool operator==(const TileUpdateParameters& a, const TileUpdateParameters& b) {
return a.minimumUpdateInterval == b.minimumUpdateInterval && a.isVolatile == b.isVolatile;
}

inline bool operator!=(const TileUpdateParameters& a, const TileUpdateParameters& b) {
return !(a == b);
}
class Tile {
public:
enum class Kind : uint8_t {
Expand All @@ -52,7 +64,8 @@ class Tile {
void setObserver(TileObserver* observer);

virtual void setNecessity(TileNecessity) {}
virtual void setMinimumUpdateInterval(Duration) {}

virtual void setUpdateParameters(const TileUpdateParameters&) {}

// Mark this tile as no longer needed and cancel any pending work.
virtual void cancel();
Expand Down
4 changes: 2 additions & 2 deletions src/mbgl/tile/tile_loader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class TileLoader {
~TileLoader();

void setNecessity(TileNecessity newNecessity);
void setMinimumUpdateInterval(Duration);
void setUpdateParameters(const TileUpdateParameters&);

private:
// called when the tile is one of the ideal tiles that we want to show definitely. the tile source
Expand All @@ -49,7 +49,7 @@ class TileLoader {
Resource resource;
std::shared_ptr<FileSource> fileSource;
std::unique_ptr<AsyncRequest> request;
Duration minimumUpdateInterval{Duration::zero()};
TileUpdateParameters updateParameters{Duration::zero(), false};
};

} // namespace mbgl
10 changes: 6 additions & 4 deletions src/mbgl/tile/tile_loader_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ void TileLoader<T>::setNecessity(TileNecessity newNecessity) {
}

template <typename T>
void TileLoader<T>::setMinimumUpdateInterval(Duration interval) {
if (minimumUpdateInterval != interval) {
minimumUpdateInterval = interval;
void TileLoader<T>::setUpdateParameters(const TileUpdateParameters& params) {
if (updateParameters != params) {
updateParameters = params;
if (hasPendingNetworkRequest()) {
// Update the pending request.
request.reset();
Expand Down Expand Up @@ -159,7 +159,9 @@ void TileLoader<T>::loadFromNetwork() {
// Instead of using Resource::LoadingMethod::All, we're first doing a CacheOnly, and then a
// NetworkOnly request.
resource.loadingMethod = Resource::LoadingMethod::NetworkOnly;
resource.minimumUpdateInterval = minimumUpdateInterval;
resource.minimumUpdateInterval = updateParameters.minimumUpdateInterval;
resource.storagePolicy =
updateParameters.isVolatile ? Resource::StoragePolicy::Volatile : Resource::StoragePolicy::Permanent;
request = fileSource->request(resource, [this](const Response& res) { loadedData(res); });
}

Expand Down
4 changes: 2 additions & 2 deletions src/mbgl/tile/vector_tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ void VectorTile::setNecessity(TileNecessity necessity) {
loader.setNecessity(necessity);
}

void VectorTile::setMinimumUpdateInterval(Duration interval) {
loader.setMinimumUpdateInterval(interval);
void VectorTile::setUpdateParameters(const TileUpdateParameters& params) {
loader.setUpdateParameters(params);
}

void VectorTile::setMetadata(optional<Timestamp> modified_, optional<Timestamp> expires_) {
Expand Down
2 changes: 1 addition & 1 deletion src/mbgl/tile/vector_tile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class VectorTile : public GeometryTile {
const Tileset&);

void setNecessity(TileNecessity) final;
void setMinimumUpdateInterval(Duration interval) final;
void setUpdateParameters(const TileUpdateParameters&) final;
void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires);
void setData(const std::shared_ptr<const std::string>& data);

Expand Down
33 changes: 33 additions & 0 deletions test/map/map.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1529,3 +1529,36 @@ TEST(Map, PlacedSymbolData) {

EXPECT_TRUE(test.frontend.getRenderer()->getPlacedSymbolsData().empty());
}

TEST(Map, VolatileSource) {
MapTest<> test{1, MapMode::Continuous};

std::atomic_int requestedTiles(0);
bool isVolatile = true;
test.fileSource->tileResponse = [&](const Resource& resource) {
auto expectedPolicy = isVolatile ? Resource::StoragePolicy::Volatile : Resource::StoragePolicy::Permanent;
EXPECT_EQ(expectedPolicy, resource.storagePolicy);
++requestedTiles;
Response res;
res.noContent = true;
return res;
};

test.map.getStyle().loadJSON(R"STYLE({
"version": 8,
"layers": [{
"id": "water",
"type": "fill",
"source": "vector",
"source-layer": "water"
}]
})STYLE");
auto source = std::make_unique<VectorSource>("vector", Tileset{{"a/{z}/{x}/{y}"}});
source->setVolatile(isVolatile);
test.map.getStyle().addSource(std::move(source));

test.map.jumpTo(CameraOptions().withZoom(16.0));
test.observer.didFinishLoadingMapCallback = [&] { test.runLoop.stop(); };
test.runLoop.run();
EXPECT_EQ(8, requestedTiles);
}
31 changes: 31 additions & 0 deletions test/storage/database_file_source.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,36 @@ TEST(DatabaseFileSource, PauseResume) {
util::Timer resumeTimer;
resumeTimer.start(Milliseconds(5), Duration::zero(), [dbfs] { dbfs->resume(); });

loop.run();
}

TEST(DatabaseFileSource, VolatileResource) {
util::RunLoop loop;

std::shared_ptr<FileSource> dbfs =
FileSourceManager::get()->getFileSource(FileSourceType::Database, ResourceOptions{});

Resource resource{Resource::Unknown, "http://127.0.0.1:3000/test", {}, Resource::LoadingMethod::CacheOnly};
Response response{};
response.data = std::make_shared<std::string>("Cached value");
std::unique_ptr<mbgl::AsyncRequest> req;

dbfs->forward(resource, response, [&] {
req = dbfs->request(resource, [&](Response res1) {
EXPECT_EQ(nullptr, res1.error);
ASSERT_TRUE(res1.data.get());
EXPECT_FALSE(res1.noContent);
EXPECT_EQ("Cached value", *res1.data);
resource.storagePolicy = Resource::StoragePolicy::Volatile;
req = dbfs->request(resource, [&](Response res2) {
req.reset();
ASSERT_TRUE(res2.error.get());
EXPECT_TRUE(res2.noContent);
EXPECT_EQ(Response::Error::Reason::NotFound, res2.error->reason);
EXPECT_EQ("Not found in offline database", res2.error->message);
loop.stop();
});
});
});
loop.run();
}
27 changes: 27 additions & 0 deletions test/storage/main_resource_loader.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,33 @@ TEST(MainResourceLoader, TEST_REQUIRES_SERVER(CacheResponse)) {
loop.run();
}

TEST(MainResourceLoader, TEST_REQUIRES_SERVER(VolatileStoragePolicy)) {
util::RunLoop loop;
MainResourceLoader fs(ResourceOptions{});

Resource resource{Resource::Unknown, "http://127.0.0.1:3000/cache"};
resource.storagePolicy = Resource::StoragePolicy::Volatile;

std::unique_ptr<AsyncRequest> req;
req = fs.request(resource, [&](Response res1) {
EXPECT_EQ(nullptr, res1.error);
ASSERT_TRUE(res1.data);
std::string firstData = *res1.data;

// Volatile resources are not stored in cache,
// so we always get new data from the server ("Response N+1").
req = fs.request(resource, [&](Response res2) {
req.reset();
EXPECT_EQ(nullptr, res2.error);
ASSERT_TRUE(res2.data);
EXPECT_NE(firstData, *res2.data);
loop.stop();
});
});

loop.run();
}

TEST(MainResourceLoader, TEST_REQUIRES_SERVER(CacheRevalidateSame)) {
util::RunLoop loop;
MainResourceLoader fs(ResourceOptions{});
Expand Down
1 change: 0 additions & 1 deletion test/storage/online_file_source.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,6 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(RespectMinimumUpdateInterval)) {
std::unique_ptr<AsyncRequest> req = fs->request(resource, [&](Response) {
auto wait = util::now() - start;
EXPECT_GE(wait, resource.minimumUpdateInterval);
EXPECT_LT(wait, resource.minimumUpdateInterval + Milliseconds(10));
loop.stop();
});

Expand Down
Loading