-
-
Notifications
You must be signed in to change notification settings - Fork 940
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(graph-sequencer): add package @pnpm/graph-sequencer #7168
Conversation
💖 Thanks for opening this pull request! 💖 |
380a7a6
to
a8bee9b
Compare
Make the change here https://github.com/pnpm/graph-sequencer instead of moving the project to the monorepo. This way I can't understand what the changes were. |
a8bee9b
to
0a582c4
Compare
This PR represents a reimplementation of the old repository, addressing previously unsupported scenarios and resolving issues related to unexpected behavior. It also includes performance enhancements. The changes have been successfully validated against the original tests and additional test cases. The reason for not submitting this PR to the old repository is due to its lack of support for TypeScript (ts), linting, and other modern tooling. Should I limit my modifications to the algorithmic aspects within the framework of the original repository?
|
The tests are failing |
0a582c4
to
3a52a2d
Compare
@zkochan done |
graph, | ||
groups: [keys], | ||
}) | ||
return graphSequencer(graph, keys) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why has the API of the function changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the older version, the 'groups' parameter more like a concept of priority. In the case of cycles within the graph, if the nodes within a cycle have different execution priorities, it is safe to execute that cycle in the order of priority. For example:
graphSequencer({
graph: new Map([
['a', ['b']],
['b', ['c']],
['c', ['a']],
]),
groups: [['a', 'b', 'c']]
})
If we use the same priority, the cycle is unsafe:
{
safe: false,
chunks: [ [ 'a' ], [ 'c' ], [ 'b' ] ],
cycles: [ [ 'a', 'b', 'c' ] ]
}
However, if we use different priorities:
groups: [['a'], ['b', 'c']]
The execution becomes safe:
{ safe: true, chunks: [ [ 'a' ], [ 'c' ], [ 'b' ] ], cycles: [] }
Currently, pnpm does not have the concept of priorities. All nodes passed as parameters have the same priority when called. Priority is not supported at the moment, but support for subgraphs (currently also requiring all nodes to be passed) is available. If you wish to add priorities in the future, it's a straightforward process - you just need to implement priority checking when identifying cycles.
But we really want 'a' to be executed first. | ||
*/ | ||
setOfKeys.has(d))] | ||
d => d !== pkgPath && setOfKeys.has(d))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why were the comments removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because both scenarios are now supported
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still retained d => d !== pkgPath && setOfKeys.has(d))]
, this is because it reduces the number of unnecessary cycles and handles external nodes. If the 'includeNodes' parameter contains nodes that are not within the graph, we cannot determine whether to discard them or treat them as separate isolated nodes. This decision should be made by the caller. If they should be discarded, they should be removed from 'includeNodes,' and if they are to be considered as standalone nodes within the graph, they should be added to the graph.
test('graph with multiple dependencies on one item', () => { | ||
expect(graphSequencer(new Map([ | ||
[0, [3]], | ||
[1, [3]], | ||
[2, []], | ||
[3, []], | ||
]), [0, 1, 2, 3])).toStrictEqual( | ||
{ | ||
safe: true, | ||
chunks: [[2, 3], [0, 1]], | ||
cycles: [], | ||
} | ||
) | ||
}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not related to your changes but I guess our script runners are not as affective as they should be at the moment.
We don't really need to wait for both 2 and 3 to finish before running 0 an 1. We only need to wait for 3 to finish and then we can continue with 0 and 1 while 2 might be still in progress.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The behavior is the same as the old version
graphSequencer({
graph: new Map([
['a', ['c']],
['b', ['c']],
['c', []],
['d', []],
]),
groups: [['a', 'b', 'c','d']]
})
// { safe: true, chunks: [ [ 'c', 'd' ], [ 'a', 'b' ] ], cycles: [] }
3a52a2d
to
2eef972
Compare
@zkochan All done, PTAL |
CONTRIBUTING.md
Outdated
@@ -36,7 +36,7 @@ sudo dnf install make automake gcc gcc-c++ kernel-devel | |||
You can run the tests of the project that you modified by going to the project's directory and running: | |||
|
|||
```shell | |||
pnpm test | |||
pnpm test-main |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be reverted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no npm command named 'test' in root project
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not about the root project.
The text says "by going to the project's directory"
Let's move the |
2eef972
to
f165671
Compare
deps/graph-sequencer/test/index.ts
Outdated
['c', []], | ||
['d', ['a']], | ||
['e', ['a', 'b', 'c']], | ||
]), ['a', 'b', 'c', 'd', 'e'])).toStrictEqual( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we make the second argument optional if all nodes are included?
deps/graph-sequencer/test/index.ts
Outdated
) | ||
}) | ||
|
||
test('graph with two self cycles and a edge link them', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test('graph with two self cycles and a edge link them', () => { | |
test('graph with two self cycles and an edge linking them', () => { |
deps/graph-sequencer/test/index.ts
Outdated
) | ||
}) | ||
|
||
test('graph with connected to each other but not a cycle', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test('graph with connected to each other but not a cycle', () => { | |
test('graph with nodes connected to each other sequentially without forming a cycle', () => { |
deps/graph-sequencer/test/index.ts
Outdated
) | ||
}) | ||
|
||
test('graph with 5 nodes and we just need 3 nodes', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test('graph with 5 nodes and we just need 3 nodes', () => { | |
test('graph sequencing with a subset of 3 nodes, ignoring 2 nodes, in a 5-node graph', () => { |
deps/graph-sequencer/test/index.ts
Outdated
) | ||
}) | ||
|
||
test('graph with no edges and we need subgraph', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test('graph with no edges and we need subgraph', () => { | |
test('graph of isolated nodes with no edges, sequencing a subgraph of selected nodes', () => { |
deps/graph-sequencer/test/index.ts
Outdated
) | ||
}) | ||
|
||
test('graph with resolved cycle subgraph', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test('graph with resolved cycle subgraph', () => { | |
test('graph with a cycle, but sequencing a subgraph that avoids the cycle', () => { |
deps/graph-sequencer/test/index.ts
Outdated
) | ||
}) | ||
|
||
test('graph with full conn', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't use "full conn" in any test. Use "fully connected"
test('graph with full conn', () => { | |
test('graph with fully connected subgraph and additional connected node', () => { |
deps/graph-sequencer/test/index.ts
Outdated
) | ||
}) | ||
|
||
test('graph with self cycle', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test('graph with self cycle', () => { | |
test('graph with self-cycle', () => { |
f165671
to
5eeba91
Compare
deps/graph-sequencer/package.json
Outdated
@@ -0,0 +1,40 @@ | |||
{ | |||
"name": "@pnpm/deps.graph-sequencer", | |||
"version": "5.0.4", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"version": "5.0.4", | |
"version": "0.0.0", |
.changeset/proud-oranges-talk.md
Outdated
@@ -0,0 +1,10 @@ | |||
--- | |||
"@pnpm/plugin-commands-rebuild": minor | |||
"@pnpm/deps.graph-sequencer": minor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"@pnpm/deps.graph-sequencer": minor | |
"@pnpm/deps.graph-sequencer": major |
deps/graph-sequencer/src/index.ts
Outdated
* @param {T[]} includeNodes - An array of nodes that should be included in the sorting process. Other nodes will be ignored. | ||
* @returns {Result<T>} An object containing the result of the sorting, including safe, chunks, and cycles. | ||
*/ | ||
export function graphSequencer<T> (graph: Graph<T>, includeNodes: T[] = [...graph.keys()]): Result<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export function graphSequencer<T> (graph: Graph<T>, includeNodes: T[] = [...graph.keys()]): Result<T> { | |
export function graphSequencer<T> (graph: Graph<T>, includedNodes: T[] = [...graph.keys()]): Result<T> { |
5eeba91
to
c59a732
Compare
Congrats on merging your first pull request! 🎉🎉🎉 |
support Subgraph Sorting for Improved Performance
Faster than https://github.com/jamiebuilds/graph-sequencer
Subgraph Sort
In scenarios where we have a large graph but only need to execute commands on a few specific nodes, the
graphSequencer
function now offers enhanced support. TheincludeNodes
parameter allows us to limit the result to only the nodes we're interested in.For instance, if our graph is a big cycle but our subgraph is a simple linked list, we can now safely execute commands on the selected nodes.
Improved Behavior
In the previous behavior of the
graphSequencer
function, there were some peculiarities when handling single-node cycles.This resulted in issues when dealing with single-node cycles, as demonstrated by the following output:
Now, with the improved behavior, each single-node cycle is treated independently, ensuring safe parallel execution: