On 2/24/13 7:07 AM, Olemis Lang wrote:
On 2/22/13, Jure Zitnik <[email protected]> wrote:
[....]
First, I agree that the environment (global, that is) setup should be
apart from product scope.
I'd agree *IF* upgrades at product scope would be desired .
Nonetheless I think they will cause a lot of trouble , so I'm strongly
against them . If we thing in terms of contracts then I strongly
recommend that the precondition for instantiating product environments
be «every setup step needed to make components work in product
contexts has been already performed» ... we'll sleep better at night
;)
Upgrades at product scope are not only desired but in my opinion
actually required (see below). Please note that each custom (3rd party
plugin) table is created *per product*. Therefor create/upgrade also
need to be handled per product.
It's a bit of a different story on product 'setup'. Now, we agreed that
the non multi-product aware components should have a separate view of
the database. In addition to system resource tables (components,
versions, tickets and such), this view also comprises of custom tables
that the component might introduce. These tables are introduced (and
later upgraded for that matter) from within IEnvironmentSetupParticipant.
This is the way I understood your approach [1]_
{{{
#!plantuml
@startuml
participant "esglobal : EnvironmentSetup" as esglobal
participant "cglobal : SomeComponent" as cglobal
participant "esproduct : EnvironmentSetup" as esproduct
participant "cproduct : SomeComponent" as cproduct
note over cglobal : cglobal.env = global environment
note over cproduct : cproduct.env = *some* product environment
== Global environment upgrade ==
esglobal -> cglobal : upgrade()
note left : Global tables as usual
== Product environment upgrade ==
esproduct -> cproduct : upgrade()
note right : Product-specific DB view
@enduml
}}}
If we are talking of such sequences then I'm -1
Yes, if I understood that sequence correctly, that's what I'm proposing
(assuming cglobal is enabled in esglobal (global) environment only and
cproduct in 'esproduct' (product) environment).
So, if we want to have a separate set of component tables per product
(this is what 'separate view of the database' implies),
Will these tables be extended with product prefix ?
Yes, table names are prefixed with product prefix. As a consequence,
each non multi-product aware component will end up with as many tables
as there are products (+1 for null/global one), each table name being
prefixed with product prefix. There is no other way of doing this as
there is no way of knowing, what the actual schema/content of the tables is.
we should use
IEnvironmentSetupParticipant interface and run it within the specific
product scope. And this would normally happen when adding product.
So
from the non multi-product aware component perspective, adding a product
would seem much like creating a new environment.
-1
that will be the source of many headaches
It is my understanding and opinion that's the only way to move forward,
excluding headaches ;)
Multi-product aware components on the other hand would be handled
differently as I described in my mail yesterday...
I was thinking of anticipating upgrades i.e. something like this [2]_
{{{
#!plantuml
@startuml
participant "es : EnvironmentSetup" as es
participant "c1 : SomeComponent" as c1
participant "c2 : OtherComponent" as c2
participant ": Multiproductsystem" as mpsys
note over c1, c2 : Setup participants
== Global environment upgrade ==
note over es : In a few words the strategy consists in anticipating
upgrades needed for MP components.
Not sure what 'anticipating' would mean in this context.
es -> c1 : upgrade()
note right : Global tables as usual
es -> mpsys : upgrade()
note right
1. Upgrades to core tables, if needed
2. Upgrades existing plugin tables to multiproduct, if needed
3. Replay MP-unaware upgrades performed in this very same upgrade
loop e.g. c1
end note
I'm not sure what the 'Replay MP-unaware upgrades' is supposed to mean
and how to get from 'c1 upgrade' to replay?
Are we supposed to keep log of SQLs that the component executed during
environment creation/upgrade? I don't think that's doable. Not to
mention that not only the SQLs would be product specific (references to
tickets or any other system table for that matter), but the component
could also be, for example, creating files in env.path.
es -> c2 : upgrade()
note right
Perform both global as well as multiproduct table upgrades
for both MP-aware and MP-unaware components.
end note
note over es
MP-aware setup participants will only need a single call to upgrade() .
'Upgrade actions' being actually product initialization yasks will not
be handled at upgrade time, but via product resource listeners instead.
end note
The latter part (single call to upgrade() for multi-product aware setup
participants + usage of product resource listeners) is what I proposed
in one of my previous mails. In my opinion (as I proposed), we will need
additional interface for multi-product aware components (could be called
'IGlobalSetupParticipant').
@enduml
}}}
... and thereby keep all this running in global scope .
I disagree, each product (setup participants) should be upgraded from
it's own scope. This is required as we want the translator to be running
in the product scope to translate all tables (SQLs) correctly.
Let's take one step back and take a look at the following use-case.
Let's assume we have a component that is a setup participant. Let's say
that non multi-product aware components are setup participants when they
implement 'IEnvironmentSetupParticipant' interface and that
multi-product aware components are setup participants when they
implement 'IGlobalSetupParticipant'. Let's also assume (for now) that
the 'IEnvironmentSetupParticipant' and 'IGlobalSetupParticipant'
interface's signatures are the same.
Let's say that the 'environment_created' method of the component does
the following:
1. creates table named 'my_table'
2. inserts into 'my_table' data that aggregates/references some data
from system tables ('ticket', 'component',...)
3. creates a file in 'env.path' that holds some environment specific data
4. inserts it's database version into 'system' table
Now, let's discuss the relevant scenarios: (global) environment creation
(install from scratch), product addition and environment upgrade. I'll
describe scenarios for both cases, first for non multi-product aware
component and then for multi-product aware component.
1. Global environment creation
-- Non multi-product aware component
In this case, the components
'IEnvironmetSetupParticipant.environment_created' should be executed in
global environment only (as there are no products). Result of this would
be 'my_table' table with aggregated/referenced data from 'null' product
scope (global scope). Note that the translator does not prefix table
names when product is 'null'. File that the component creates would be
stored in the global environment path, the 'system' table would get a
record with 'null' product prefix.
-- Multi-product aware component
Same as in case of non multi-product aware component, everything happens
in global environment and through (new) 'IGlobalSetupParticipant'
interface. Results of 'environment_created' (tables, files, etc) are the
same as in the case of non multi-product aware component.
2. Product (with 'MYPRODUCT' prefix) addition
-- Non multi-product aware component
Adding a product should invoke component's
'IEnvironmentSetupParticipant.environment_created' method from within
'ProductEnvironment('MYPRODUCT')'. This would enable SQL translator in
that scope with the known rules. Result of this would be
'MYPRODUCT_my_table' with aggregated/referenced data from 'MYPRODUCT's
scope. File would be stored in 'MYPRODUCT' product's path. 'system'
table would get a record with 'MYPRODUCT' product prefix.
-- Multi-product aware component
Multi-product aware component is notified of product addition through
IResourceChangeListener.
3. Environment upgrade
-- Non multi-product aware component
Following the normal process (in 'trac.env.Environment'), each non
multi-product setup participant should be upgraded (if required) from
all defined product scopes. This would leverage
'IEnvironmentSetupParticipant.environment_needs_upgrade' and
'IEnvironmentSetupParticipant.upgrade_environment', executing those two
from within all product scopes (+global one).
In case of the aforementioned component, the component would, in
'environment_needs_upgrade', normally do a 'SELECT' on 'system' table.
As it would be running from within product scope, it would get version
for that specific product. The view of the database would be scoped to
that specific product so any update, alter or whatever database
operation it would perform would be limited to that specific scope.
'INSERT's or 'UPDATE's into 'system' table during the upgrade would also
behave as it is the case in product addition, it'd be limited to the
specific product's scope.
Note that upgrades for all products could be run from within the same
database transaction. The 'needs_upgrade' and 'upgrade' methods of
'trac.env.Environment' should be overriden to provide product aware
upgrade process.
-- Multi-product aware component
Multi-product aware component's 'IGlobalSetupParticipant' is only
invoked once, in global environment scope.
This is how, in my understanding, things should be. I'm not sure there's
really any other way of implementing this ...
Cheers,
Jure