static const double FailedAttempt = 10*60;
}
-typedef map<string,time_t> CompletedTiles;
+typedef map<string,time_t> TileAgeCache;
///////////////////////////////////////////////////////////////////////////////
// helper functions ///////////////////////////////////////////////////////////
// commond helpers between both internal and external models
- bool isPathCached(const SyncItem& next) const;
+ SyncItem::Status isPathCached(const SyncItem& next) const;
void initCompletedTilesPersistentCache();
void writeCompletedTilesPersistentCache() const;
void updated(SyncItem item, bool isNewDirectory);
void fail(SyncItem failedItem);
+ void notFound(SyncItem notFoundItem);
bool _use_built_in;
HTTP::Client _http;
volatile bool _is_dirty;
volatile bool _stop;
SGBlockingDeque <SyncItem> waitingTiles;
- CompletedTiles _completedTiles;
+
+ TileAgeCache _completedTiles;
+ TileAgeCache _notFoundItems;
+
SGBlockingDeque <SyncItem> _freshTiles;
bool _use_svn;
string _svn_server;
if (_stop)
break;
- if (isPathCached(next)) {
+ SyncItem::Status cacheStatus = isPathCached(next);
+ if (cacheStatus != SyncItem::Invalid) {
_cache_hits++;
SG_LOG(SG_TERRAIN, SG_DEBUG,
"Cache hit for: '" << next._dir << "'");
- next._status = SyncItem::Cached;
+ next._status = cacheStatus;
_freshTiles.push_back(next);
_is_dirty = true;
continue;
// check result
SVNRepository::ResultCode res = slot.repository->failure();
if (res == SVNRepository::SVN_ERROR_NOT_FOUND) {
- // this is fine, but maybe we should use a different return code
- // in the future to higher layers can distinguish this case
- slot.currentItem._status = SyncItem::NotFound;
- _freshTiles.push_back(slot.currentItem);
- _is_dirty = true;
+ notFound(slot.currentItem);
} else if (res != SVNRepository::SVN_NO_ERROR) {
fail(slot.currentItem);
} else {
// drain the waiting tiles queue into the sync slot queues.
while (!waitingTiles.empty()) {
SyncItem next = waitingTiles.pop_front();
- if (isPathCached(next)) {
+ SyncItem::Status cacheStatus = isPathCached(next);
+ if (cacheStatus != SyncItem::Invalid) {
_cache_hits++;
- SG_LOG(SG_TERRAIN, SG_DEBUG, "TerraSync Cache hit for: '" << next._dir << "'");
- next._status = SyncItem::Cached;
+ SG_LOG(SG_TERRAIN, SG_DEBUG, "\nTerraSync Cache hit for: '" << next._dir << "'");
+ next._status = cacheStatus;
_freshTiles.push_back(next);
_is_dirty = true;
continue;
} // of thread running loop
}
-bool SGTerraSync::SvnThread::isPathCached(const SyncItem& next) const
+SyncItem::Status SGTerraSync::SvnThread::isPathCached(const SyncItem& next) const
{
- CompletedTiles::const_iterator ii = _completedTiles.find( next._dir );
+ TileAgeCache::const_iterator ii = _completedTiles.find( next._dir );
if (ii == _completedTiles.end()) {
- return false;
+ ii = _notFoundItems.find( next._dir );
+ // Invalid means 'not cached', otherwise we want to return to
+ // higher levels the cache status
+ return (ii == _notFoundItems.end()) ? SyncItem::Invalid : SyncItem::NotFound;
}
- // check if the path still physically exists
+ // check if the path still physically exists. This is needed to
+ // cope with the user manipulating our cache dir
SGPath p(_local_dir);
p.append(next._dir);
if (!p.exists()) {
- return false;
+ return SyncItem::Invalid;
}
time_t now = time(0);
- return (ii->second > now);
+ return (ii->second > now) ? SyncItem::Cached : SyncItem::Invalid;
}
void SGTerraSync::SvnThread::fail(SyncItem failedItem)
_is_dirty = true;
}
+void SGTerraSync::SvnThread::notFound(SyncItem item)
+{
+ // treat not found as authorative, so use the same cache expiry
+ // as succesful download. Important for MP models and similar so
+ // we don't spam the server with lookups for models that don't
+ // exist
+
+ time_t now = time(0);
+ item._status = SyncItem::NotFound;
+ _freshTiles.push_back(item);
+ _is_dirty = true;
+ _notFoundItems[ item._dir ] = now + UpdateInterval::SuccessfulAttempt;
+ writeCompletedTilesPersistentCache();
+}
+
void SGTerraSync::SvnThread::updated(SyncItem item, bool isNewDirectory)
{
time_t now = time(0);
for (int i=0; i<cacheRoot->nChildren(); ++i) {
SGPropertyNode* entry = cacheRoot->getChild(i);
+ bool isNotFound = (strcmp(entry->getName(), "not-found") == 0);
string tileName = entry->getStringValue("path");
time_t stamp = entry->getIntValue("stamp");
if (stamp < now) {
continue;
}
- _completedTiles[tileName] = stamp;
+ if (isNotFound) {
+ _completedTiles[tileName] = stamp;
+ } else {
+ _notFoundItems[tileName] = stamp;
+ }
}
}
}
SGPropertyNode_ptr cacheRoot(new SGPropertyNode);
- CompletedTiles::const_iterator it = _completedTiles.begin();
+ TileAgeCache::const_iterator it = _completedTiles.begin();
for (; it != _completedTiles.end(); ++it) {
SGPropertyNode* entry = cacheRoot->addChild("entry");
entry->setStringValue("path", it->first);
entry->setIntValue("stamp", it->second);
}
+ it = _notFoundItems.begin();
+ for (; it != _notFoundItems.end(); ++it) {
+ SGPropertyNode* entry = cacheRoot->addChild("not-found");
+ entry->setStringValue("path", it->first);
+ entry->setIntValue("stamp", it->second);
+ }
+
writeProperties(f, cacheRoot, true /* write_all */);
f.close();
}