# Sharing objects across taks with Configuration Cache enabled

**URL:** <https://discuss.gradle.org/t/sharing-objects-across-taks-with-configuration-cache-enabled/48598>\
**Category:** Help/Discuss\
**Created:** [June 1, 2024, 11:33am UTC](https://discuss.gradle.org/t/sharing-objects-across-taks-with-configuration-cache-enabled/48598 "2024-06-01T11:33:55Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![fmckee](https://avatars.discourse-cdn.com/v4/letter/f/f14d63/32.png) [@fmckee](https://discuss.gradle.org/u/fmckee)\
**Post date:** [June 1, 2024, 11:33am UTC](https://discuss.gradle.org/t/sharing-objects-across-taks-with-configuration-cache-enabled/48598/1 "2024-06-01T11:33:55Z")

</div>

Hi,

I’m running into an issue with objects that are shared across multiple tasks.

A Plugin creates a TestListener that is used by a custom task and the test tasks in the project:

```gradle
class ExamplePlugin implements org.gradle.api.Plugin<Project> {

    @Override
    public void apply(Project project) {
        var testListener = new ExampleTestListener();

        project.getTasks().register("exampleTask", ExampleTask.class, task -> {
            task.getTestListener().set(testListener);
        });
        project.getTasks().withType(Test.class, testTask -> {
            testTask.finalizedBy("exampleTask");
            testTask.addTestListener(testListener);
        });
    }

}

```

`ExampleTestListener` keeps track of the tests that have been executed:

```gradle
class ExampleTestListener implements TestListener {
    
    List<TestDescriptor> tests = new ArrayList<>();

    ...
 
    @Override
    public void afterTest(TestDescriptor testDescriptor, TestResult testResult) {
        System.err.println("[ExampleTestListener] Test task uses instance %s".formatted(this));
        tests.add(testDescriptor);
    }
}

```

`ExampleTask` logs the data collected by `ExampleTestListener`:

```gradle
abstract class ExampleTask extends DefaultTask {

    @Internal
    abstract Property<ExampleTestListener> getTestListener();

    @Inject
    public ExampleTask() {
        doLast(task -> {
            getLogger().lifecycle("[ExampleTask] Referenced ExampleTestListener instance: %s".formatted(getTestListener().get()));
            getLogger().lifecycle("[ExampleTask] Test count: %s".formatted(getTestListener().get().tests.size()));
        });
    }

}

```

# Execution (Configuration Cache disabled)

```gradle
./gradlew clean test --no-configuration-cache

> Task :test
[ExampleTestListener] Test task uses instance com.example.ExampleTestListener@60e6df11

FooTest > testSomething() PASSED

> Task :exampleTask
[ExampleTask] Referenced ExampleTestListener instance: com.example.ExampleTestListener@60e6df11
[ExampleTask] Test count: 1

```

# Execution (Configuration Cache enabled)

```gradle
./gradlew clean test --configuration-cache        
Calculating task graph as no configuration cache is available for tasks: clean test

> Task :test
[ExampleTestListener] Test task uses instance com.example.ExampleTestListener@51c45d55

FooTest > testSomething() PASSED

> Task :exampleTask
[ExampleTask] Referenced ExampleTestListener instance: com.example.ExampleTestListener@1fcdb88
[ExampleTask] Test count: 0

```

With Configuration Cache enabled, the Test tasks and `ExampleTask` reference different instances:

- `ExampleTestListener@51c45d55`
- `ExampleTestListener@1fcdb88`

This causes `ExampleTask` to read 0 tests being executed.

Sample repo: [https://github.com/skippy-io/gradle-configuration-cache-issue](https://github.com/skippy-io/gradle-configuration-cache-issue)

How can I ensure that `ExampleTasks` sees the data written by the test tasks when Configuration Cache is enabled?

Thanks in advance!

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [June 1, 2024, 5:26pm UTC](https://discuss.gradle.org/t/sharing-objects-across-taks-with-configuration-cache-enabled/48598/2 "2024-06-01T17:26:44Z")

</div>

With configuration cache the state of each task is serialized and persisted and then deserialized for usage.  
During this deserialization both tasks of course get different instances created.

Instead use a [shared build service](https://docs.gradle.org/current/userguide/build_services.html) for sharing data between tasks that are not shared via task outputs / inputs.

---

<div class="post-metadata">

**Author:** ![fmckee](https://avatars.discourse-cdn.com/v4/letter/f/f14d63/32.png) [@fmckee](https://discuss.gradle.org/u/fmckee)\
**Post date:** [June 2, 2024, 10:34am UTC](https://discuss.gradle.org/t/sharing-objects-across-taks-with-configuration-cache-enabled/48598/3 "2024-06-02T10:34:50Z")

</div>

Thank you for the response. Just read up on shared build services - that indeed sounds like the solution for my problem.

Before I go down the route of a shared build service: Can this be solved by customizing the (de-)serialization that is used by the build cache? It seems odd that the serialization ignores instance fields.

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [June 2, 2024, 10:50am UTC](https://discuss.gradle.org/t/sharing-objects-across-taks-with-configuration-cache-enabled/48598/4 "2024-06-02T10:50:04Z")

</div>

What do you mean with “it ignores instance fields”?

---

<div class="post-metadata">

**Author:** ![fmckee](https://avatars.discourse-cdn.com/v4/letter/f/f14d63/32.png) [@fmckee](https://discuss.gradle.org/u/fmckee)\
**Post date:** [June 2, 2024, 11:00am UTC](https://discuss.gradle.org/t/sharing-objects-across-taks-with-configuration-cache-enabled/48598/5 "2024-06-02T11:00:20Z")

</div>

The test task adds data to the `tests` field:

> <https://github.com/skippy-io/gradle-configuration-cache-issue/blob/3266aa0abc2d175adc71e79a40395452bac4ec32/buildSrc/src/main/java/com/example/ExampleTestListener.java#L28>

My assumption how this should work:

- `ExampleTestListener$12345` is created
- Test task modifies the `tests` field in `ExampleTestListener$12345`
- `ExampleTestListener$12345` is serialized (including the `tests` field)
- `ExampleTestListener$67890` is de-serialized (`tests` field is restored)
- `ExampleTask` checks `ExampleTestListener$67890` and sees the data written by the Test task

Hope that clears it up.

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [June 2, 2024, 12:53pm UTC](https://discuss.gradle.org/t/sharing-objects-across-taks-with-configuration-cache-enabled/48598/6 "2024-06-02T12:53:24Z")

</div>

It clears up what you understand, yes, but that’s not how it happens.

First all up to and including the configuration phase is executed.  
Then the state of the tasks is serialized into the configuration cache, so that all that is executed up to now can be skipped for further invocations.

At this point in time the test task did not run and did not write any instance fields.  
The instances in both tasks are the same but both with empty `tests` fields.

If the state of tasks is then deserialized from the CC, both tasks get individual instances deserialized, both with empty lists.

---

<div class="post-metadata">

**Author:** ![fmckee](https://avatars.discourse-cdn.com/v4/letter/f/f14d63/32.png) [@fmckee](https://discuss.gradle.org/u/fmckee)\
**Post date:** [June 2, 2024, 1:15pm UTC](https://discuss.gradle.org/t/sharing-objects-across-taks-with-configuration-cache-enabled/48598/7 "2024-06-02T13:15:41Z")

</div>

Thanks for the detailed explanation. I will take a stab at your proposed solution (shared build service).

Your feedback has been very helpful 👍
