Will a set of test workers always have strictly mo...
# community-support
c
Will a set of test workers always have strictly monotonic increasing numbers for test workers via the
org.gradle.test.worker
system property?
For maxForks = N, I'm trying to create an index by doing
worker % N
but it seems like two workers might get the same index!?
which might mean that worker numbers are not strictly monotonic
i.e. workers have numbers [1, 3, 5, 7]
b
Not sure I understand the question. Workers are also used for task execution. So it could be that worker 2, 4, and 6 are busy executing other tasks at the moment test forks are created.
v
Those are not test workers though, so I would expect that they don't Mom no in share the same number space. But a test workers could probably be quit or die and then a new one started.
c
I'm trying to determine a database URL based on index, hoping to have isolation for every test worker and to do that, I'm relying on (or rather hoping for) the strictly monotonic nature of the test worker numbers, to then do a modulo forkCount in order to determine an index from 1 to N
v
It is probably strictly monotonic, but strictly monotonic does not mean no gaps, especially as one worker is closed or dies and another one is started
c
ohh, gaps are possible?
so the worker number is not guaranteed to be between
X ... X+N
where
N
is
test.maxParallelForks
?
any hint what I could be looking for in the logs to understand if that happened?
v
I don't know whether gaps per-se are possible, but again, afair you can also configure it to start a new worker process every Y tests and I'd doubt a new worker will get the same number but a new one.
So you maybe have maximum of N test workers at the same time, but not a maximum of N test workers at all
But I might misremember of course 🙂
c
so in a bigger build, with parallel tasks, it could happen that if two test tasks run, I get N+1 test workers?
and then could happen that the task with maxParallelForks = N has gaps?
v
Again, I don't know whether gaps are possible, but you can also with only one task in one project get more than N test workers, just not at the same time iirc.
c
well, I'm having a weird situation that seems to suggest that two workers have a gap
where N = 2
and a gap of 1 is obviously enough to cause trouble with shared resources that depend on isolation
let me ask you differently, is there another possibility to make this (pooling DB schemas for isolated test workers) work with Gradle?
I have N DB schemas named x1...xn and want N workers to use these
v
I have no idea, but for sure not using
org.gradle.test.worker
. I just tried with
Copy code
maxParallelForks = 1
forkEvery = 1
and output the value and it was 13 and 14 on first run and 15 and 16 on rerun. So the test workers are also per daemon lifetime, not per build execution. And at least with parallel task execution there can for sure also be gaps I'd say now.
c
I don't use forkEvery
v
That was an example
A quick test
Maybe you should not use the parallelism on Gradle level, but on the Test framework level like the parallel running support in Spock or Jupiter. Then you can do that resource sharing in-process.
c
I might have to do that if Gradle doesn't offer a better solution indeed, but I'd prefer to let Gradle handle parallelization if possible
Looking at
MaxNParallelTestClassProcessor
and
ForkingTestClassProcessor
, it seems that since
WorkerProcessFactory#idGenerator
is shared, there can indeed be gaps
so do you know a way to set some kind of "index" property on the workers?
our tests sometimes change JVM wide settings, like
TimeZone
for the scope of a test and dealing with that when using JUnit Jupiter parallelization is a bit painful, hence my preference to stick to JVM forking
v
I have no idea, I would never use the Gradle parallelization as it majorly lacks behind test framework parallelization. For example if you have 50 tests and 5 max parallel forks, Gradle cannot know how to best split it so that you leverage resources best. It will just give the 10 tests to each fork. But it could well be that the 10 given to the first worker happen to need 1 minute per test while the other 90 tests are all together finished within 4 seconds, so executing the tests needs 10 minutes while with the test-framework parallelization the tests would be finished within 1 minute. Of course you have to write the tests in a way they can run in parallel and use the resource locking for tests that must not run in parallel as they depend on the same shared resource like the timezone setting or system properties.
Maybe you can have some additional process that manages the DB connections and the test processes then ask that process for which database connection to use or similar.
Or you have some on-disk database where you record which process uses which DB connection and then get one from there instead of the separate process.
c
thanks for the hints. I just read about the JUnit
@Isolated
annotation which can be put on such global state mutating tests to make them run in isolation. I'll try that then.
For example if you have 50 tests and 5 max parallel forks, Gradle cannot know how to best split it so that you leverage resources best. It will just give the 10 tests to each fork.
That's a good point, I realized this already, but it was mostly good enough for my use cases
👌 1
v
Yes,
@Isolated
you can use of course, but you disable parallelity for those of course. If you use an explicit resource lock on all tests that depend on such global state, then only those are not run in parallel, and even more if you can split by read-lock and read-write-lock.
👍 1