The myth of a small deployment image using metacello

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

The myth of a small deployment image using metacello

NorbertHartl
I have a seaside project that will be installed in a typical fashion headless on a server somewhere on the net. The project is in its final stage so I started to care about the size of the image. Or better: The size of the image is not my main concern but the number of packages installed. The less packages you load the better it is, it is that simple.
This time I encountered a problem that I often see but this particular one explains in a very good way that how we use metacello can be improved.

In my project I load only Seaside-Core. This has the advantage of loading far less packages and I don't have the need to remove a bunch of registered web applications that registered themselves when being loaded.
I make use of a bootstrap template bought somewhere on the net. I use magritte a lot so I was happy to see that there is a package called BootstrapMagritte. The package extends some magritte ui classes and overwrites some methods to add specific classes to HTML tags. Basically not very much code.
What happended after I added the package as a dependency to my own project you can see here in the load directive of metacello

linear load :
        explicit load : 0.2.3 [ConfigurationOfBootstrapMagritte]
        linear load : 0.2.3 [ConfigurationOfBootstrapMagritte]
                linear load : 3.5.0 [ConfigurationOfMagritte3]
                        linear load : 1.1.0 [ConfigurationOfMagritteGlamourForPharo40]
                                load : Glamour-Presentations.issue_1126-SeanDeNigris.178
                                load : Glamour-Morphic-Renderer.issue_1126-SeanDeNigris.325
                        explicit load : 3.1.5 [ConfigurationOfSeaside3]
                        linear load : 3.1.5 [ConfigurationOfSeaside3]
                                explicit load : 0.26 [ConfigurationOfParasol]
                                linear load : 0.26 [ConfigurationOfParasol]
                                        linear load : 3.2.0 [ConfigurationOfSeaside3]
                                load : Seaside-Tests-REST-Core-EstebanMaringolo.34

This is wrong in so many ways. I load a little extensions to my web application and end up loading _all_ of seaside which loads beach parasol (a ui testing framework), _all_ of magritte which in turn loads glamour packages (a ui toolkit). So at this point I think WTF?

Why does this happen? Because the "default" group in many projects contains all of the packages and dependencies of the project. I thought at first this is wrong but in the meantime I think it is right. Whenever a configuration is loaded without additional parameters the "default" group gets loaded. This is the best scenario for newcomers (e.g. using the catalog to load). They don't know better but want to see what the package has to offer. Fine, we should support that.

For managing projects this gets annoying really quick like you can see above. It would always load all dependencies and all packages of every project that your own project depends. This is a no-go. To circumvent this a lot of people add a "core" group to their configurations (loading the essential parts). This is great because in my own project I can add a dependency to a project and load only the "core" group. This works if the dependency chain is not very long (at best it works in depth 1 ;) ). As soon as a project has two interesting groups the problem gets worse. A good example is magritte. It is useful for describing your model as well as it contains useful seaside components. The moment I add a dependency from my model package to magritte seaside I know it is wrong. I find myself sometimes duplicating dependencies just to mitigate the effect. I do something like

                        project: 'Magritte3Seaside' with: [
                                spec
                                        className: #ConfigurationOfMagritte3;
                                        versionString: #'stable';
                                        loads: #('Seaside' );
                                        repository: 'http://smalltalkhub.com/mc/Magritte/Magritte3/main/' ];
                        project: 'Magritte3Core' with: [
                                spec
                                        className: #ConfigurationOfMagritte3;
                                        versionString: #'stable';
                                        loads: #('Core' );
                                        repository: 'http://smalltalkhub.com/mc/Magritte/Magritte3/main/' ];

and…
                        package: #'MyProject-Model-Core' with: [
                                spec requires: #( 'Magritte3Core' ). ];
                        package: #'MyProject-UI-Core' with: [
                                spec requires: #( 'Seaside3' 'Magritte3Seaside' 'BootstrapMagritte'  ). ];


just to be able to put the right dependencies. But this way is cumbersome to do and not support by tools like Versionner.

The main problem to me is that the maintainer of the configuration defines the granularity of pieces to load forcing me to do stupid workarounds to achieve what I want. To me the problem is that the separation is not done properly. I think the logic of having loads() in the baseline and a pure string like "Magritte3Core" in the dependency spec is reversed. It should be

                        project: 'Magritte3' with: [
                                spec
                                        className: #ConfigurationOfMagritte3;
                                        versionString: #'stable';
                                        repository: 'http://smalltalkhub.com/mc/Magritte/Magritte3/main/' ];

and…

                        package: #'MyProject-Model-Core' with: [
                                spec requires: #( 'Magritte3:Core' ). ];
                        package: #'MyProject-UI-Core' with: [
                                spec requires: #( 'Seaside3' 'Magritte3:Seaside' 'BootstrapMagritte'  ). ];

Note the "Magritte3:Core" in the dependency spec. The meaning is that there is a dependency to Magritte3 and group Core. This way dependencies can be properly aligned. Or are there other way to deal with it?

What do you think?

Norbert
Reply | Threaded
Open this post in threaded view
|

Re: The myth of a small deployment image using metacello

Stephan Eggermont-3
On 24/06/16 15:28, Norbert Hartl wrote:
> The main problem to me is that the maintainer of the configuration
> defines the granularity of pieces to load forcing me to do stupid
> workarounds to achieve what I want.

Who else should do that? Dependency management with Metacello is
uni-directional. I agree that it is important to define
those dependencies as specific as possible, and have to recognize
that they will always err on the side of loading too much.

Please update BootstrapMagritte to be more granular.
I've only used it with lots of dependencies.

Stephan


Reply | Threaded
Open this post in threaded view
|

Re: The myth of a small deployment image using metacello

Dale Henrichs-3
In reply to this post by NorbertHartl


On 6/24/16 6:28 AM, Norbert Hartl wrote:

> I have a seaside project that will be installed in a typical fashion headless on a server somewhere on the net. The project is in its final stage so I started to care about the size of the image. Or better: The size of the image is not my main concern but the number of packages installed. The less packages you load the better it is, it is that simple.
> This time I encountered a problem that I often see but this particular one explains in a very good way that how we use metacello can be improved.
>
> In my project I load only Seaside-Core. This has the advantage of loading far less packages and I don't have the need to remove a bunch of registered web applications that registered themselves when being loaded.
> I make use of a bootstrap template bought somewhere on the net. I use magritte a lot so I was happy to see that there is a package called BootstrapMagritte. The package extends some magritte ui classes and overwrites some methods to add specific classes to HTML tags. Basically not very much code.
> What happended after I added the package as a dependency to my own project you can see here in the load directive of metacello
>
> linear load :
> explicit load : 0.2.3 [ConfigurationOfBootstrapMagritte]
> linear load : 0.2.3 [ConfigurationOfBootstrapMagritte]
> linear load : 3.5.0 [ConfigurationOfMagritte3]
> linear load : 1.1.0 [ConfigurationOfMagritteGlamourForPharo40]
> load : Glamour-Presentations.issue_1126-SeanDeNigris.178
> load : Glamour-Morphic-Renderer.issue_1126-SeanDeNigris.325
> explicit load : 3.1.5 [ConfigurationOfSeaside3]
> linear load : 3.1.5 [ConfigurationOfSeaside3]
> explicit load : 0.26 [ConfigurationOfParasol]
> linear load : 0.26 [ConfigurationOfParasol]
> linear load : 3.2.0 [ConfigurationOfSeaside3]
> load : Seaside-Tests-REST-Core-EstebanMaringolo.34
>
> This is wrong in so many ways. I load a little extensions to my web application and end up loading _all_ of seaside which loads beach parasol (a ui testing framework), _all_ of magritte which in turn loads glamour packages (a ui toolkit). So at this point I think WTF?
>
> Why does this happen? Because the "default" group in many projects contains all of the packages and dependencies of the project. I thought at first this is wrong but in the meantime I think it is right. Whenever a configuration is loaded without additional parameters the "default" group gets loaded. This is the best scenario for newcomers (e.g. using the catalog to load). They don't know better but want to see what the package has to offer. Fine, we should support that.
>
> For managing projects this gets annoying really quick like you can see above. It would always load all dependencies and all packages of every project that your own project depends. This is a no-go. To circumvent this a lot of people add a "core" group to their configurations (loading the essential parts). This is great because in my own project I can add a dependency to a project and load only the "core" group. This works if the dependency chain is not very long (at best it works in depth 1 ;) ). As soon as a project has two interesting groups the problem gets worse. A good example is magritte. It is useful for describing your model as well as it contains useful seaside components. The moment I add a dependency from my model package to magritte seaside I know it is wrong. I find myself sometimes duplicating dependencies just to mitigate the effect. I do something like
>
> project: 'Magritte3Seaside' with: [
> spec
> className: #ConfigurationOfMagritte3;
> versionString: #'stable';
> loads: #('Seaside' );
> repository: 'http://smalltalkhub.com/mc/Magritte/Magritte3/main/' ];
> project: 'Magritte3Core' with: [
> spec
> className: #ConfigurationOfMagritte3;
> versionString: #'stable';
> loads: #('Core' );
> repository: 'http://smalltalkhub.com/mc/Magritte/Magritte3/main/' ];
>
> and…
> package: #'MyProject-Model-Core' with: [
> spec requires: #( 'Magritte3Core' ). ];
> package: #'MyProject-UI-Core' with: [
> spec requires: #( 'Seaside3' 'Magritte3Seaside' 'BootstrapMagritte'  ). ];
>
>
> just to be able to put the right dependencies. But this way is cumbersome to do and not support by tools like Versionner.
>
> The main problem to me is that the maintainer of the configuration defines the granularity of pieces to load forcing me to do stupid workarounds to achieve what I want. To me the problem is that the separation is not done properly. I think the logic of having loads() in the baseline and a pure string like "Magritte3Core" in the dependency spec is reversed. It should be
>
> project: 'Magritte3' with: [
> spec
> className: #ConfigurationOfMagritte3;
> versionString: #'stable';
> repository: 'http://smalltalkhub.com/mc/Magritte/Magritte3/main/' ];
>
> and…
>
> package: #'MyProject-Model-Core' with: [
> spec requires: #( 'Magritte3:Core' ). ];
> package: #'MyProject-UI-Core' with: [
> spec requires: #( 'Seaside3' 'Magritte3:Seaside' 'BootstrapMagritte'  ). ];
>
> Note the "Magritte3:Core" in the dependency spec. The meaning is that there is a dependency to Magritte3 and group Core. This way dependencies can be properly aligned. Or are there other way to deal with it?
>
> What do you think?
>
> Norbert
Norbert,

Does the following meet your needs?

                        project: 'Magritte3' with: [
                                spec
                                        className: #ConfigurationOfMagritte3;
                                        versionString: #'stable';
                                        repository: 'http://smalltalkhub.com/mc/Magritte/Magritte3/main/' ];

                        project: 'Magritte3Seaside'
                                copyFrom: 'Magritte'
                                with: [ spec loads: #('Seaside' ) ];
                        project: 'Magritte3Core'
                                copyFrom: 'Magritte'
                                with: [ spec loads: ##('Core' ) ];


I assume the "stupid workaround" is the fact that in your example you had to specify Magritte twice? #project:copyFrom:with: has been around for a long time in Metacello and was invented to solve this particular problem ... FWIW, this feature is documented in Lesson12 of "Metacello Configuration Tutorial Part 2" in the Help Browser ...

Dale


Reply | Threaded
Open this post in threaded view
|

Re: The myth of a small deployment image using metacello

Denis Kudriashov
Hi Dale.

2016-06-24 21:53 GMT+02:00 Dale Henrichs <[hidden email]>:
Does the following meet your needs?

                        project: 'Magritte3' with: [
                                spec
                                        className: #ConfigurationOfMagritte3;
                                        versionString: #'stable';
                                        repository: 'http://smalltalkhub.com/mc/Magritte/Magritte3/main/' ];

                        project: 'Magritte3Seaside'
                                copyFrom: 'Magritte'
                                with: [ spec loads: #('Seaside' ) ];
                        project: 'Magritte3Core'
                                copyFrom: 'Magritte'
                                with: [ spec loads: ##('Core' ) ];


I assume the "stupid workaround" is the fact that in your example you had to specify Magritte twice? #project:copyFrom:with: has been around for a long time in Metacello and was invented to solve this particular problem ... FWIW, this feature is documented in Lesson12 of "Metacello Configuration Tutorial Part 2" in the Help Browser ...

Yes, this is pattern which we all must use. But problem that it is duplication of groups definition in all dependant projects. 
Norbert suggest solution for this by referencing group name inside dependency list (spec requires: #( 'Magritte3:Core' )). I really like this idea.

Reply | Threaded
Open this post in threaded view
|

Re: The myth of a small deployment image using metacello

Dale Henrichs-3



On 8/31/16 4:34 AM, Denis Kudriashov wrote:
Hi Dale.

2016-06-24 21:53 GMT+02:00 Dale Henrichs <[hidden email]>:
Does the following meet your needs?

                        project: 'Magritte3' with: [
                                spec
                                        className: #ConfigurationOfMagritte3;
                                        versionString: #'stable';
                                        repository: 'http://smalltalkhub.com/mc/Magritte/Magritte3/main/' ];

                        project: 'Magritte3Seaside'
                                copyFrom: 'Magritte'
                                with: [ spec loads: #('Seaside' ) ];
                        project: 'Magritte3Core'
                                copyFrom: 'Magritte'
                                with: [ spec loads: ##('Core' ) ];


I assume the "stupid workaround" is the fact that in your example you had to specify Magritte twice? #project:copyFrom:with: has been around for a long time in Metacello and was invented to solve this particular problem ... FWIW, this feature is documented in Lesson12 of "Metacello Configuration Tutorial Part 2" in the Help Browser ...

Yes, this is pattern which we all must use. But problem that it is duplication of groups definition in all dependant projects. 
Norbert suggest solution for this by referencing group name inside dependency list (spec requires: #( 'Magritte3:Core' )). I really like this idea.

Denis, thanks for clarifying. I can definitely see that 'Magritte3:Core' is more convenient ...

Could you add a Metacello issue[1] with these details? I don't have any major Metacello work scheduled in the near term so it will probably be a while before I implement such a feature. At first blush it would seem to require a bit of rework in the project name space implementation.

A pull request with an implementation would be even better:)

Dale

[1] https://github.com/dalehenrich/metacello-work/issues/new