Skip to content
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

Repro for Duplicated item inserted into OrderedSet #2958

Closed
Quafadas opened this issue Jan 8, 2024 · 6 comments · Fixed by #2959
Closed

Repro for Duplicated item inserted into OrderedSet #2958

Quafadas opened this issue Jan 8, 2024 · 6 comments · Fixed by #2959
Milestone

Comments

@Quafadas
Copy link
Contributor

Quafadas commented Jan 8, 2024

@lefou Aha! I believe I have a semi-minimal reproduction for this on a public repository.

  1. git clone https://github.com/Quafadas/vecxt.git
  2. To prove that main works (you don't need to do this)
    • mill -j 1 vecxt.jvm.prepareOffline
  3. git checkout build_error
  4. mill clean
  5. mill -j 1 vecxt.jvm.prepareOffline

The first time I do this, I get

simon@Simons-Mac-mini vecxt % mill clean
[1/1] clean 
simon@Simons-Mac-mini vecxt % mill -j 1 vecxt.jvm.prepareOffline
[23/23] vecxt.jvm.prepareOffline 
An unexpected error occurred
Exception in thread "MillServerActionRunner" java.lang.Exception: Duplicated item inserted into OrderedSet: mill.define.Task$TraverseCtx@630efd4
        at mill.api.AggWrapper$Agg$Mutable.append(AggWrapper.scala:99)
        at mill.api.AggWrapper$Agg$Mutable$.$anonfun$from$1(AggWrapper.scala:60)
        at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)
        at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)
        at scala.collection.AbstractIterator.foreach(Iterator.scala:1300)
        at mill.api.AggWrapper$Agg$Mutable$.from(AggWrapper.scala:60)
        at mill.api.AggWrapper$Agg$.from(AggWrapper.scala:56)
        at mill.eval.EvaluatorCore.evaluate0(EvaluatorCore.scala:170)
        at mill.eval.EvaluatorCore.$anonfun$evaluate$1(EvaluatorCore.scala:43)
        at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
        at mill.eval.EvaluatorCore.evaluate(EvaluatorCore.scala:34)
        at mill.eval.EvaluatorCore.evaluate$(EvaluatorCore.scala:26)
        at mill.eval.EvaluatorImpl.evaluate(EvaluatorImpl.scala:15)
        at mill.main.RunScript$.evaluateNamed(RunScript.scala:38)
        at mill.main.RunScript$.$anonfun$evaluateTasksNamed$2(RunScript.scala:26)
        at scala.util.Either.map(Either.scala:382)
        at mill.main.RunScript$.evaluateTasksNamed(RunScript.scala:26)
        at mill.runner.MillBuildBootstrap$.evaluateWithWatches(MillBuildBootstrap.scala:399)
        at mill.runner.MillBuildBootstrap.$anonfun$processFinalTargets$3(MillBuildBootstrap.scala:308)
        at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
        at mill.runner.MillBuildBootstrap.processFinalTargets(MillBuildBootstrap.scala:308)
        at mill.runner.MillBuildBootstrap.evaluateRec(MillBuildBootstrap.scala:196)
        at mill.runner.MillBuildBootstrap.evaluate(MillBuildBootstrap.scala:48)
        at mill.runner.MillMain$.$anonfun$main0$6(MillMain.scala:234)
        at mill.runner.Watching$.watchLoop(Watching.scala:27)
        at mill.runner.MillMain$.$anonfun$main0$1(MillMain.scala:219)
        at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
        at scala.Console$.withErr(Console.scala:193)
        at mill.api.SystemStreams$.$anonfun$withStreams$2(SystemStreams.scala:62)
        at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
        at scala.Console$.withOut(Console.scala:164)
        at mill.api.SystemStreams$.$anonfun$withStreams$1(SystemStreams.scala:61)
        at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
        at scala.Console$.withIn(Console.scala:227)
        at mill.api.SystemStreams$.withStreams(SystemStreams.scala:60)
        at mill.runner.MillMain$.main0(MillMain.scala:101)
        at mill.runner.MillServerMain$.main0(MillServerMain.scala:83)
        at mill.runner.MillServerMain$.main0(MillServerMain.scala:35)
        at mill.runner.Server.$anonfun$handleRun$1(MillServerMain.scala:187)
        at java.base/java.lang.Thread.run(Thread.java:1583)

Second and subsequent incarnations of vecxt.jvm.prepareOffline succeed, I suspect because something is correctly cached.

I can reproduce, by cleaning and re-running the command. It's also with -j 1, so I hope easier to work with.

I now suspect, it can be minimised further, but that's the best I have for right now - I'll have a look at something truly minimal in the coming days, if I can... but I can't see a reason this shouldn't be the same for anyone else, too.

@lefou
Copy link
Member

lefou commented Jan 8, 2024

Thanks for the reproducer!

lefou added a commit that referenced this issue Jan 8, 2024
Ensure when flatMap-ing, that we don't append duplicates.
Whether this is correct needs yet to be analyzed, but in general,
we don't enfore unique-ness of task results, and even when we evaluate
disjuct tasks, we can't assume that all results are disjuct, hence
using a strict Agg, which enforces uniqueness on append seems not
correct.

Here's my reasoning: If we have two tasks A and B, and B just forwards
the result of A as it's own result, both tasks return the same result.
@lefou
Copy link
Member

lefou commented Jan 8, 2024

@Quafadas I have a quick fix for you to try. It should solve your issue, although the solution should be discusses and improved.

@Quafadas
Copy link
Contributor Author

Quafadas commented Jan 8, 2024

Er - blimey. that as quick. Once the CI goes though I'll try and track down the hash, and take it for spin :-)...

Cool - thanks!

@lefou
Copy link
Member

lefou commented Jan 9, 2024

Minimized reproduction:

// file: scratch/build.sc
import mill._, mill.scalalib._
import mill.define.ModuleRef

val repos = T.task {
  Seq(
    coursier.maven.MavenRepository("https://repo1.maven.org/maven2"),
    coursier.LocalRepositories.ivy2Local
  )
}

object CustomZincWorkerModule extends ZincWorkerModule with CoursierModule {
  def repositoriesTask = repos
}

object foo extends JavaModule {
  def repositoriesTask = repos
  def zincWorker       = ModuleRef(CustomZincWorkerModule)
}
> mill -i dev.run scratch -i clean \\+ foo.prepareOffline
[1725/1725] dev.run 
[info] compiling 1 Scala source to /tmp/mill/scratch/out/mill-build/compile.dest/classes ...
[info] done compiling
An unexpected error occurred
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at mill.main.client.IsolatedMillMainLoader.runMain(IsolatedMillMainLoader.java:58)
        at mill.main.client.MillClientMain.main(MillClientMain.java:78)
Caused by: java.lang.Exception: Duplicated item inserted into OrderedSet: mill.define.Task$TraverseCtx@724e483f
        at mill.api.AggWrapper$Agg$Mutable.append(AggWrapper.scala:99)
        at mill.api.AggWrapper$Agg$Mutable$.$anonfun$from$1(AggWrapper.scala:60)
        at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)
        at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)
        at scala.collection.AbstractIterator.foreach(Iterator.scala:1300)
        at mill.api.AggWrapper$Agg$Mutable$.from(AggWrapper.scala:60)
        at mill.api.AggWrapper$Agg$.from(AggWrapper.scala:56)
        at mill.eval.EvaluatorCore.evaluate0(EvaluatorCore.scala:172)
        at mill.eval.EvaluatorCore.$anonfun$evaluate$1(EvaluatorCore.scala:43)
        at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
        at mill.eval.EvaluatorCore.evaluate(EvaluatorCore.scala:34)
        at mill.eval.EvaluatorCore.evaluate$(EvaluatorCore.scala:26)
        at mill.eval.EvaluatorImpl.evaluate(EvaluatorImpl.scala:15)
        at mill.main.RunScript$.evaluateNamed(RunScript.scala:38)
        at mill.main.RunScript$.$anonfun$evaluateTasksNamed$2(RunScript.scala:26)
        at scala.util.Either.map(Either.scala:382)
        at mill.main.RunScript$.evaluateTasksNamed(RunScript.scala:26)
        at mill.runner.MillBuildBootstrap$.evaluateWithWatches(MillBuildBootstrap.scala:399)
        at mill.runner.MillBuildBootstrap.$anonfun$processFinalTargets$3(MillBuildBootstrap.scala:308)
        at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
        at mill.runner.MillBuildBootstrap.processFinalTargets(MillBuildBootstrap.scala:308)
        at mill.runner.MillBuildBootstrap.evaluateRec(MillBuildBootstrap.scala:196)
        at mill.runner.MillBuildBootstrap.evaluate(MillBuildBootstrap.scala:48)
        at mill.runner.MillMain$.$anonfun$main0$6(MillMain.scala:234)
        at mill.runner.Watching$.watchLoop(Watching.scala:27)
        at mill.runner.MillMain$.$anonfun$main0$1(MillMain.scala:219)
        at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
        at scala.Console$.withErr(Console.scala:193)
        at mill.api.SystemStreams$.$anonfun$withStreams$2(SystemStreams.scala:62)
        at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
        at scala.Console$.withOut(Console.scala:164)
        at mill.api.SystemStreams$.$anonfun$withStreams$1(SystemStreams.scala:61)
        at scala.util.DynamicVariable.withValue(DynamicVariable.scala:59)
        at scala.Console$.withIn(Console.scala:227)
        at mill.api.SystemStreams$.withStreams(SystemStreams.scala:60)
        at mill.runner.MillMain$.main0(MillMain.scala:101)
        at mill.runner.MillMain$.liftedTree1$1(MillMain.scala:78)
        at mill.runner.MillMain$.main(MillMain.scala:69)
        at mill.runner.MillMain.main(MillMain.scala)
        ... 6 more

@lefou
Copy link
Member

lefou commented Jan 9, 2024

I guess, this issue is, that the anonymous task repos ends up in the dependencies of multiple other tasks and as such in different terminal groups.

And indeed, if I change val repos to def repos, it's always a new instance and the exception goes away.

lefou added a commit that referenced this issue Jan 9, 2024
lefou added a commit that referenced this issue Jan 10, 2024
Anonymous tasks can be defined as `val` or `def`. This is in contrast to
named targets, which always need to be defined as `def`.

Also, anonymous tasks end up in the terminal group of the target that's
using them.

If the same anonymous task is used in multiple targets, it also ends up
multiple time in the task results. When a `Evaluator.Results` is
created, this clashes, due to the use of a `Strict.Agg` for
`Evalutor.Results.evaluated`.

This change deduplicates the results before constructing the
`Evaluator.Results`.

Pull request: #2959
@lefou lefou added this to the 0.11.7 milestone Jan 10, 2024
@Quafadas
Copy link
Contributor Author

@lefou thankyou! Looked a little more painful than expected... but👏, very excited to try it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants