Skip to content
Snippets Groups Projects
Unverified Commit 8b6ef7b3 authored by lelemm's avatar lelemm Committed by GitHub
Browse files

Removed recursion in place for iterable solution to prevent stack overflow (#2848)

parent 6ad0b47c
No related branches found
No related tags found
No related merge requests found
...@@ -90,6 +90,9 @@ export function ExperimentalFeatures() { ...@@ -90,6 +90,9 @@ export function ExperimentalFeatures() {
Goal templates Goal templates
</FeatureToggle> </FeatureToggle>
<FeatureToggle flag="simpleFinSync">SimpleFIN sync</FeatureToggle> <FeatureToggle flag="simpleFinSync">SimpleFIN sync</FeatureToggle>
<FeatureToggle flag="iterableTopologicalSort">
Iterable topological sort budget
</FeatureToggle>
</View> </View>
) : ( ) : (
<Link <Link
......
...@@ -9,6 +9,7 @@ const DEFAULT_FEATURE_FLAG_STATE: Record<FeatureFlag, boolean> = { ...@@ -9,6 +9,7 @@ const DEFAULT_FEATURE_FLAG_STATE: Record<FeatureFlag, boolean> = {
customReports: false, customReports: false,
spendingReport: false, spendingReport: false,
simpleFinSync: false, simpleFinSync: false,
iterableTopologicalSort: true,
}; };
export function useFeatureFlag(name: FeatureFlag): boolean { export function useFeatureFlag(name: FeatureFlag): boolean {
......
import { getPrefs } from '../prefs';
// @ts-strict-ignore // @ts-strict-ignore
export function Graph() { export function Graph() {
const graph = { const graph = {
...@@ -76,14 +78,38 @@ export function Graph() { ...@@ -76,14 +78,38 @@ export function Graph() {
return graph; return graph;
} }
function topologicalSortUntil(name, visited, sorted) { function topologicalSort(sourceNodes) {
const visited = new Set();
const sorted = [];
const prefs = getPrefs();
const iterableTopologicalSort =
prefs != null ? prefs['flags.iterableTopologicalSort'] : false;
sourceNodes.forEach(name => {
if (!visited.has(name)) {
if (iterableTopologicalSort) {
topologicalSortIterable(name, visited, sorted);
} else {
topologicalSortUntil(name, visited, sorted, 0);
}
}
});
return sorted;
}
function topologicalSortUntil(name, visited, sorted, level) {
visited.add(name); visited.add(name);
if (level > 2500) {
console.error('Limit of recursions reached while sorting budget: 2500');
return;
}
const iter = adjacent(name).values(); const iter = adjacent(name).values();
let cur = iter.next(); let cur = iter.next();
while (!cur.done) { while (!cur.done) {
if (!visited.has(cur.value)) { if (!visited.has(cur.value)) {
topologicalSortUntil(cur.value, visited, sorted); topologicalSortUntil(cur.value, visited, sorted, level + 1);
} }
cur = iter.next(); cur = iter.next();
} }
...@@ -91,17 +117,54 @@ export function Graph() { ...@@ -91,17 +117,54 @@ export function Graph() {
sorted.unshift(name); sorted.unshift(name);
} }
function topologicalSort(sourceNodes) { function topologicalSortIterable(name, visited, sorted) {
const visited = new Set(); const stackTrace: StackItem[] = [];
const sorted = [];
sourceNodes.forEach(name => { stackTrace.push({
if (!visited.has(name)) { count: -1,
topologicalSortUntil(name, visited, sorted); value: name,
} parent: '',
level: 0,
}); });
return sorted; while (stackTrace.length > 0) {
const current = stackTrace.slice(-1)[0];
const adjacents = adjacent(current.value);
if (current.count === -1) {
current.count = adjacents.size;
}
if (current.count > 0) {
const iter = adjacents.values();
let cur = iter.next();
while (!cur.done) {
if (!visited.has(cur.value)) {
stackTrace.push({
count: -1,
parent: current.value,
value: cur.value,
level: current.level + 1,
});
} else {
current.count--;
}
cur = iter.next();
}
} else {
if (!visited.has(current.value)) {
visited.add(current.value);
sorted.unshift(current.value);
}
const removed = stackTrace.pop();
for (let i = 0; i < stackTrace.length; i++) {
if (stackTrace[i].value === removed.parent) {
stackTrace[i].count--;
}
}
}
}
} }
function generateDOT() { function generateDOT() {
...@@ -113,11 +176,18 @@ export function Graph() { ...@@ -113,11 +176,18 @@ export function Graph() {
}); });
return ` return `
digraph G { digraph G {
${edgeStrings.join('\n').replace(/!/g, '_')} ${edgeStrings.join('\n').replace(/!/g, '_')}
} }
`; `;
} }
return graph; return graph;
} }
interface StackItem {
count: number;
value: string;
parent: string;
level: number;
}
...@@ -5,7 +5,8 @@ export type FeatureFlag = ...@@ -5,7 +5,8 @@ export type FeatureFlag =
| 'goalTemplatesEnabled' | 'goalTemplatesEnabled'
| 'customReports' | 'customReports'
| 'spendingReport' | 'spendingReport'
| 'simpleFinSync'; | 'simpleFinSync'
| 'iterableTopologicalSort';
export type LocalPrefs = Partial< export type LocalPrefs = Partial<
{ {
......
---
category: Bugfix
authors: [lelemm]
---
Remove recursion from topological sort to prevent stack overflow
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment