-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Change Layer interface from &self to &mut self
This Commit is an experiment to see the effects of moving the interface for `Layer` to be mutable. The primary driver for wanting to mutate a layer is a desire for stateful logging. I hit this in the consuming state-machine interface, and previously, Josh hit it in a logging interface designed to track indentation level. Upside: - Developers are now allowed to do whatever they want within a layer - It's more technically correct to require a single reference for these functions. With the prior interface, it would be theoretically possible to execute the same Layer in parallel (though unlikely) - We can now change things between function calls. Downside: - Mutable self means there's no guarantee something didn't change between function calls. ## Alternatives ### Alternatives: Consuming interface Originally, I wanted to make these consuming interfaces, but to do that, we would have to change to taking a `Box<Self>` as you cannot use `Self` directly in a trait: ``` fn create( self: Box<Self>, context: &BuildContext<Self::Buildpack>, layer_path: &Path, ) -> Result<LayerResult<Self::Metadata>, <Self::Buildpack as Buildpack>::Error>; ``` We could explore going down that path. This change was easier to make to start the discussion. ### Alternatives: Mutable and immutable layer traits Provide a `Layer` and `MutLayer` trait to let developers pick what they want. You would also need a `context.handle_mut_layer` function as well. It's nice in that it gives devs the flexibility to choose whether they need that mutability. They could start with `Layer` and change to `MutLayer` only if needed. The downside is that maintaining duplicate logic that implements both would be annoying. There's no good way to express "this could be mutable, or not, I don't care which" To Rust. ### Alternatives: Exploded layer API The current layer implementation works as a state machine. You put state in (values in the struct that implements Layer). Libcnb uses that input along with the prior run's metadata, and the existing_layer_strategy is used to determine the cache state (keep or clear) and which function should be called (create or update). For workflows suited to this "simple input equals simple output," it works well. In other cases, where the developer might be doing something surprising or unusual (example, https://github.com/heroku/buildpacks-ruby/blob/c9ee5ff29890d30f2ccb23f850ed5274c11b06de/buildpacks/ruby/src/layers/bundle_install_layer.rs#L192-L198) it requires the developer to squeeze their use case into the state machine concept. A mutable logger is one such concept that is currently impossible to represent using the existing layer signature. When I wrote a competing CNB library in Ruby, I didn't try to bundle metadata, caching logic, disk state, and env vars together in one thing. Instead, I separated them to be acted on individually. I'm linking them here for reference. I do NOT suggest we try to duplicate these interfaces: - Metadata: https://github.com/heroku/buildpacks-ruby/blob/22d489df6ad59c82220e9e3801bbb399afd78882/lib/heroku_buildpack_ruby/metadata/cnb.rb - Env: https://github.com/heroku/buildpacks-ruby/blob/22d489df6ad59c82220e9e3801bbb399afd78882/lib/heroku_buildpack_ruby/env_proxy/default.rb This approach was flawed and could be better. However, it presents what an inverted or exploded layer API could look like. Instead of trying to provide a transactional state machine view into a layer, we could expose primitives and let users build their own. Or possibly provide several different approaches to working with layers: - Individual functions (i.e., inverted/exploded API) - Current state machine Layer interface - Some other access patterns we've yet to explore. For that last bullet point, we should invest in more specific and opinionated solutions to individual use cases instead of a singular pattern. One example is cache invalidation. I've said for a long time that a proc macro that compares structs would be useful for general cache invalidation. It doesn't even have to be tied to CNB cache invalidation. Ultimately, I eventually want more than what we've currently got. I've found ways to hack around some limitations and provide varying interfaces: - Pre-built Layer for ONLY setting env vars: https://github.com/heroku/buildpacks-ruby/blob/c9ee5ff29890d30f2ccb23f850ed5274c11b06de/commons/src/layer/configure_env_layer.rs - Exploded "give me a path" Layer for use in caching public assets https://github.com/heroku/buildpacks-ruby/blob/c9ee5ff29890d30f2ccb23f850ed5274c11b06de/commons/src/cache/in_app_dir_cache_layer.rs I've not made a PR using an alternative Layer API because I've not managed to produce one that I like, and others might want something different. I don't know if anyone has been able to produce a good sketch of what that "something different" could look like. ## Implementation and analysis I changed the `layer/public_interface.rs` and then fixed the cascading failures. It was very straightforward, and there was no change in logic needed (just changes in mutability). It's not a backward-compatible change, but the update will be easy.
- Loading branch information
Showing
11 changed files
with
34 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters