]> git.mxchange.org Git - simgear.git/commitdiff
Explicitly track not-found responses from SVN.
authorJames Turner <zakalawe@mac.com>
Thu, 23 Oct 2014 13:42:55 +0000 (14:42 +0100)
committerJames Turner <zakalawe@mac.com>
Mon, 1 Dec 2014 17:49:03 +0000 (17:49 +0000)
When SVN reports a path is not found (ocean tile), track
this data explicitly and cache the result. Reduces backend
hits for missing tiles.

simgear/scene/tsync/terrasync.cxx

index 0cbd0511d0c15bdbcee27320b8178887c9ef251e..e5eb651d86762d962ba47ca3b0362597d4443996 100644 (file)
@@ -84,7 +84,7 @@ namespace UpdateInterval
     static const double FailedAttempt     = 10*60;
 }
 
-typedef map<string,time_t> CompletedTiles;
+typedef map<string,time_t> TileAgeCache;
 
 ///////////////////////////////////////////////////////////////////////////////
 // helper functions ///////////////////////////////////////////////////////////
@@ -260,11 +260,12 @@ private:
 
     // 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;
@@ -273,7 +274,10 @@ private:
    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;
@@ -488,11 +492,12 @@ void SGTerraSync::SvnThread::runExternal()
         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;
@@ -556,11 +561,7 @@ void SGTerraSync::SvnThread::updateSyncSlot(SyncSlot &slot)
         // 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 {
@@ -622,10 +623,11 @@ void SGTerraSync::SvnThread::runInternal()
         // 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;
@@ -652,22 +654,26 @@ void SGTerraSync::SvnThread::runInternal()
     } // 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)
@@ -681,6 +687,21 @@ 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);
@@ -718,13 +739,18 @@ void SGTerraSync::SvnThread::initCompletedTilesPersistentCache()
     
     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;
+        }
     }
 }
 
@@ -741,13 +767,20 @@ void SGTerraSync::SvnThread::writeCompletedTilesPersistentCache() const
     }
     
     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();
 }