Bash

git add builder/src/build_index.cpp && git commit -m "$(cat <<'EOF'
fix: greedy-first with full backtracking retry from clean state

Greedy pass runs first (fast). If it leaves unused sub-ways, a second
pass runs pure backtracking from a clean state (fresh used flags).
Keep whichever pass produces more rings. This handles the 10 remaining
planet cases where greedy makes wrong branch choices without the
performance penalty of backtracking-first.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EOF
)" && cat builder/src/build_index.cpp | ssh node3 "cat > /tmp/traccar-geocoder/builder/src/build_index.cpp" && ssh node3 "cd /tmp/traccar-geocoder/builder/build && make -j\$(nproc) 2>&1 | tail -3" 2>&1

Commit, upload, build


[main bdb2e8d] fix: greedy-first with full backtracking retry from clean state
 1 file changed, 53 insertions(+), 33 deletions(-)
      |           ^~~~~~~~
[100%] Linking CXX executable build-index
[100%] Built target build-index


ssh node3 "rm -rf ~/planet-parallel6 && mkdir -p ~/planet-parallel6 && nohup /tmp/traccar-geocoder/builder/build/build-index ~/planet-parallel6 ~/pbf/planet.osm.pbf --parallel-admin > ~/planet-parallel6.log 2>&1 & echo started" 2>&1

Start planet build with greedy+retry


Command running in background with ID: b20atht9m. Output is being written to: /tmp/claude-0/-root-traccar-geocoder/af1c4baa-0f9a-4420-a8b3-ce20dc28c8b9/tasks/b20atht9m.output


sleep 600 && ssh node3 "grep -E 'Assembled 77|Parallel admin|polygon|Done' ~/planet-parallel6.log | tail -5; ps aux | grep 'build-index.*planet-parallel6' | grep -v grep | awk '{print \$3\"% CPU\", \$10}'" 2>&1

Check after 10 min


  Added 15275 admin polygons from closed ways.
  Assembling admin polygons in parallel (778863 relations, 3098498 way geometries)...
  Assembled 770K admin relations...
0.0% CPU 0:00
1613% CPU 167:19


At 770K with 167 min CPU. Let me check if it's stuck or progressing.


sleep 60 && ssh node3 "grep -c 'Assembled 78' ~/planet-parallel6.log" 2>&1

Check if past 770K


Exit code 1
0


Stuck again at 770K. The backtracking retry (pass 2) is running for the complex relation at this point and hitting the 100K budget on many sub-ways. The issue is that pass 2 runs backtracking on ALL relations, not just the ones where greedy left unused sub-ways.

Wait — pass 2 only triggers if there are unused sub-ways. But the complex relation at 770K has hundreds of sub-ways where the greedy succeeded on some rings but consumed sub-ways needed by others. The backtracking retry then tries ALL sub-ways from scratch and hits the budget repeatedly.

The fix: skip pass 2 for relations where the greedy produced the same number of rings as sub-way chains (no unused sub-ways). Or increase the per-relation budget timeout.

Actually, the simplest fix: the 100K budget is per-ring-attempt. A relation with 100 sub-ways generates 100 ring attempts × 100K calls = 10 billion operations. I need to add a per-relation total budget.


ssh node3 "pkill -f 'build-index.*planet-parallel6'; echo killed" 2>&1

Kill stuck build


Exit code 255


ssh node3 "kill \$(pgrep -f 'build-index.*planet-parallel6' | tail -1); echo killed" 2>&1

Kill by PID


killed
bash: line 1: kill: (4712) - No such process


The backtracking retry needs a TOTAL budget across all ring attempts in a relation. Let me add that.


   687→            !ring_has_self_intersection(ring)) {
   688→            rings.push_back(std::move(ring));
   689→        } else {
   690→            for (auto wi : attempt_used) used[wi] = false;
   691→        }
   692→    }
   693→
   694→    // Pass 2: If greedy left unused sub-ways, retry from scratch with backtracking.
   695→    // Backtracking makes correct branch choices but is slower.
   696→    {
   697→        bool any_unused = false;
   698→        for (size_t i = 0; i < used.size(); i++) {
   699→            if (!used[i]) { any_unused = true; break; }
   700→        }
   701→        if (any_unused) {
   702→            // Try pure backtracking from clean state
   703→            std::vector<bool> bt_used(sub_ways.size(), false);
   704→            std::vector<std::vector<std::pair<double,double>>> bt_rings;
   705→
   706→            BacktrackState bt2{sub_ways, coord_adj, bt_used, coord_key, 0};
   707→
   708→            for (size_t si = 0; si < sub_ways.size(); si++) {
   709→                if (bt_used[si]) continue;
   710→                const auto& sg = sub_ways[si];
   711→                int64_t fk = coord_key(sg.coords.front().first, sg.coords.front().second);
   712→                int64_t lk = coord_key(sg.coords.back().first, sg.coords.back().second);
   713→                if (sg.coords.size() >= 4 && fk == lk) {
   714→                    bt_used[si] = true;
   715→                    bt_rings.push_back(sg.coords);
   716→                    continue;
   717→                }
   718→                bt_used[si] = true;
   719→                bt2.calls = 0;
   720→                std::vector<std::pair<size_t, bool>> path;
   721→                if (bt2.try_close(fk, lk, path, 0)) {
   722→                    std::vector<std::pair<double,double>> r = sg.coords;
   723→                    for (const auto& [wi, rev] : path) {
   724→                        const auto& c = sub_ways[wi].coords;
   725→                        if (!rev) r.insert(r.end(), c.begin() + 1, c.end());
   726→                        else for (auto it2 = c.rbegin() + 1; it2 != c.rend(); ++it2) r.push_back(*it2);

            !ring_has_self_intersection(ring)) {
            rings.push_back(std::move(ring));
        } else {
            for (auto wi : attempt_used) used[wi] = false;
        }
    }

    // Pass 2: If greedy left unused sub-ways, retry from scratch with backtracking.
    // Backtracking makes correct branch choices but is slower.
    {
        bool any_unused = false;
        for (size_t i = 0; i < used.size(); i++) {
            if (!used[i]) { any_unused = true; break; }
        }
        if (any_unused) {
            // Try pure backtracking from clean state
            std::vector<bool> bt_used(sub_ways.size(), false);
            std::vector<std::vector<std::pair<double,double>>> bt_rings;

            BacktrackState bt2{sub_ways, coord_adj, bt_used, coord_key, 0};

            for (size_t si = 0; si < sub_ways.size(); si++) {
                if (bt_used[si]) continue;
                const auto& sg = sub_ways[si];
                int64_t fk = coord_key(sg.coords.front().first, sg.coords.front().second);
                int64_t lk = coord_key(sg.coords.back().first, sg.coords.back().second);
                if (sg.coords.size() >= 4 && fk == lk) {
                    bt_used[si] = true;
                    bt_rings.push_back(sg.coords);
                    continue;
                }
                bt_used[si] = true;
                bt2.calls = 0;
                std::vector<std::pair<size_t, bool>> path;
                if (bt2.try_close(fk, lk, path, 0)) {
                    std::vector<std::pair<double,double>> r = sg.coords;
                    for (const auto& [wi, rev] : path) {
                        const auto& c = sub_ways[wi].coords;
                        if (!rev) r.insert(r.end(), c.begin() + 1, c.end());
                        else for (auto it2 = c.rbegin() + 1; it2 != c.rend(); ++it2) r.push_back(*it2);


Edit

            BacktrackState bt2{sub_ways, coord_adj, bt_used, coord_key, 0};

            for (size_t si = 0; si < sub_ways.size(); si++) {
                if (bt_used[si]) continue;
                const auto& sg = sub_ways[si];
                int64_t fk = coord_key(sg.coords.front().first, sg.coords.front().second);
                int64_t lk = coord_key(sg.coords.back().first, sg.coords.back().second);
                if (sg.coords.size() >= 4 && fk == lk) {
                    bt_used[si] = true;
                    bt_rings.push_back(sg.coords);
                    continue;
                }
                bt_used[si] = true;
                bt2.calls = 0;
                std::vector<std::pair<size_t, bool>> path;

            BacktrackState bt2{sub_ways, coord_adj, bt_used, coord_key, 0};
            int total_bt_calls = 0;
            constexpr int MAX_TOTAL_BT_CALLS = 500000; // total budget for entire relation

            for (size_t si = 0; si < sub_ways.size(); si++) {
                if (bt_used[si]) continue;
                if (total_bt_calls >= MAX_TOTAL_BT_CALLS) break; // budget exceeded
                const auto& sg = sub_ways[si];
                int64_t fk = coord_key(sg.coords.front().first, sg.coords.front().second);
                int64_t lk = coord_key(sg.coords.back().first, sg.coords.back().second);
                if (sg.coords.size() >= 4 && fk == lk) {
                    bt_used[si] = true;
                    bt_rings.push_back(sg.coords);
                    continue;
                }
                bt_used[si] = true;
                bt2.calls = 0;
                std::vector<std::pair<size_t, bool>> path;