| 733 | } |
| 734 | |
| 735 | std::vector<DataProcessorService::TransformRecipe> DataProcessorService::transformsDependingOn( |
| 736 | const std::vector<std::string>& removed_series) const { |
| 737 | // Two names "touch" if one is the other, or one is a topic and the other a |
| 738 | // "topic/field" under it (handles both topic-vs-field directions). |
| 739 | const auto touches = [](const std::string& a, const std::string& b) { |
| 740 | if (a == b) { |
| 741 | return true; |
| 742 | } |
| 743 | if (b.size() > a.size() && b.compare(0, a.size(), a) == 0 && b[a.size()] == '/') { |
| 744 | return true; |
| 745 | } |
| 746 | return a.size() > b.size() && a.compare(0, b.size(), b) == 0 && a[b.size()] == '/'; |
| 747 | }; |
| 748 | |
| 749 | std::vector<std::string> affected(removed_series.begin(), removed_series.end()); |
| 750 | std::vector<TransformRecipe> result; |
| 751 | std::unordered_set<std::string> seen_keys; |
| 752 | |
| 753 | // Fixpoint: a transform is pulled in when an input touches any affected name; |
| 754 | // its own outputs then become affected, so children (derivative-of-derivative) |
| 755 | // are caught on a later pass. Bounded by the number of recipes. |
| 756 | bool grew = true; |
| 757 | while (grew) { |
| 758 | grew = false; |
| 759 | for (const auto& [key, recipe] : transform_recipes_) { |
| 760 | if (recipe.ephemeral || seen_keys.count(key) > 0) { |
| 761 | continue; |
| 762 | } |
| 763 | bool depends = false; |
| 764 | for (const auto& in : recipe.inputs) { |
| 765 | for (const auto& name : affected) { |
| 766 | if (touches(in, name)) { |
| 767 | depends = true; |
| 768 | break; |
| 769 | } |
| 770 | } |
| 771 | if (depends) { |
| 772 | break; |
| 773 | } |
| 774 | } |
| 775 | if (depends) { |
| 776 | result.push_back(recipe); |
| 777 | seen_keys.insert(key); |
| 778 | for (const auto& out : recipe.outputs) { |
| 779 | affected.push_back(out); |
| 780 | } |
| 781 | grew = true; |
| 782 | } |
| 783 | } |
| 784 | } |
| 785 | return result; |
| 786 | } |
| 787 | |
| 788 | Expected<DataProcessorService::TransformRecipe> DataProcessorService::restoreTransform(const TransformRecipe& recipe) { |
| 789 | TransformRecipe restored = recipe; // copy; installTransform re-derives the runtime fields |