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

Provider gets evaluated twice with configuration cache enabled #20820

Closed
antohaby opened this issue May 17, 2022 · 3 comments
Closed

Provider gets evaluated twice with configuration cache enabled #20820

antohaby opened this issue May 17, 2022 · 3 comments

Comments

@antohaby
Copy link

antohaby commented May 17, 2022

A Gradle Task with some field of type Provider<*> get evaluated twice if Configuration Cache is enabled.

Expected Behavior

Provider gets evaluated only once.

Context

I want to delay the time-expensive evaluation of some data. Either during task execution (when configuration cache isn't enabled)
or capture it in configuration cache and then reuse cached value during execution.

Steps to Reproduce

// build.gradle.kts
abstract class MyTask : DefaultTask() {
    private val prop = project.provider {
        println("Expensive data calculation")
        "data"
    }

    @TaskAction
    fun action() {
        println("Results: ${prop.get()}")
    }
}

val myTask by tasks.registering(MyTask::class)

Executing this task for the first time shows that prop gets evaluated twice: during configuration phase and execution phase

$ ./gradlew myTask  --configuration-cache --rerun-tasks
Configuration cache is an incubating feature.
Calculating task graph as no configuration cache is available for tasks: myTask
Expensive data calculation

> Task :myTask
Expensive data calculation
Results: data

Executing this task second time shows that prop is recovered from cache and thus no evaluation happened.

$ ./gradlew myTask  --configuration-cache --rerun-tasks                
Configuration cache is an incubating feature.
Reusing configuration cache.

> Task :myTask
Results: data

Workaround

Kotlin lazy delegate works as expected. Although gradle configuration cache got a bit polluted with internal state of Lazy.

abstract class MyTask : DefaultTask() {
    private val prop by lazy {
        println("Expensive data calculation")
        "data"
    }

    @TaskAction
    fun action() {
        println("Results: $prop")
    }
}

val myTask by tasks.registering(MyTask::class)

1st execution:

$ ./gradlew myTask  --configuration-cache --rerun-tasks
Configuration cache is an incubating feature.
Calculating task graph as configuration cache cannot be reused because file 'build.gradle.kts' has changed.
Expensive data calculation

> Task :myTask
Results: data

2nd execution:

$ ./gradlew myTask  --configuration-cache --rerun-tasks
Configuration cache is an incubating feature.
Reusing configuration cache.

> Task :myTask
Results: data

Environment

Gradle 7.4.2

@antohaby
Copy link
Author

One major limitation is that I can't use project inside project.provider {}.

abstract class MyTask : DefaultTask() {
    private val prop by lazy { project.scrapData() }
    /* ... */
}

This task would fail in the strict mode because project inside lambda is accessed during execution due to the original bug of double evaluation.

@hegyibalint hegyibalint added in:configuration-cache Configuration Caching and removed to-triage labels May 18, 2022
@Vampire
Copy link
Contributor

Vampire commented Jun 27, 2022

A provider is calculated each time its value is requested, that is by design afaik.
But you can relatively easy produce a cached provider that is only calculated lazily once:

inline fun <reified T> cachedProvider(crossinline block: () -> T): Provider<T> =
    objects
        .property<T>()
        .value(provider { block() })
        .apply {
            disallowChanges()
            finalizeValueOnRead()
        }

@bamboo bamboo added this to the 8.1 RC1 milestone Mar 9, 2023
@bamboo
Copy link
Member

bamboo commented Mar 9, 2023

Fixed by #21985

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

No branches or pull requests

4 participants