Replies: 10 comments 15 replies
-
Another upside of the profile-based approach is that we could even pre-load all the storage factories, instantiating one for each profile and then computing the |
Beta Was this translation helpful? Give feedback.
-
Thanks for proposing this change. Once mapStorage gets traction, I see that there will be multiple instances of a variant like JPA or LDAP most of the time. My comments on this: I like the solution where a configuration is stated centrally in mapStorage and is then referenced in other places as it prevents duplicating the configuration by an ID. To name this ID a profile is fine for me, or it could be just named "id". One thing I didn't see in the examples you provided: How would I configure a store per realm? I can image that users want to map a realm to a database, or in LDAP scenarios would map a realm to a specific LDAP directory or subtree. While this question is quite important for me from an LDAP point of view, feel free to explain if this is not related to this question. |
Beta Was this translation helpful? Give feedback.
-
I've crafted a draft PR with the changes proposed here - #10429. Testing locally I was able to have some models (role) in one DB, and others (client, clientScope) in a different DB. It needs some tidying up but it gives an idea of what would be needed to make it work. |
Beta Was this translation helpful? Give feedback.
-
Thank you @sguilhen for the proposal. I very much like the idea of ability to declare profiles within a system configuration. I would like to stress the scope of this proposal: The proposal is limited for use in system configuration; configuration profile declaration is not suitable for realm components (since component configuration is not a tree but a map of configuration options, and profile declaration effectively builds a tree structure). There are few comments / clarifications needed, and I will add those as separate answers to this discussion so that they can be discussed independently. |
Beta Was this translation helpful? Give feedback.
-
Default configurationThe default profile configuration should be indistinguishable from the same configuration of provider when there is no profile defined, i.e. the following two configs should be equivalent for achieving "postgres-dev" configuration: "mapStorage": {
...
"jpa-map-storage": {
"driver": "XYZ",
"datasource": "FOO"
# configuration of the postgres-dev database.
} }, and "mapStorage": {
...
"jpa-map-storage": {
"driver": "XYZ",
"datasource": "FOO",
// configuration same as of the postgres-dev profile.
"profile": {
"postgres-dev": {
// "datasource": "FOO" <-- this has been here originally and should not have been, moved above
// this profile configuration is intentionally left empty
},
"postgres-prod": {
"datasource": "BAR"
// configuration of the postgres prod database.
} } } }, From that it seems to me that there is no need for WDYT? |
Beta Was this translation helpful? Give feedback.
-
Distinguishing when to cope with configuration as reference and when as a new componentHow would the code mutually distinguish the following different use cases of the configuration?
|
Beta Was this translation helpful? Give feedback.
-
Compatibility with component configurationRe: Area-specific storage provider configuration
provider=jpa-map-storage
provider-profile=postgres-prod
jpa-map-storage.option1=value1
jpa-map-storage.option2=value2
... This means that for the respective provider factory, rather than the That is also the reason why the storage component configuration is flattened in map storage and behaves as if that was like this: "role": {
"provider": "map",
"map": {
"storage": {
"provider": "jpa-map-storage",
"provider-profile": "postgres-prod",
"option1": "value1"
// alternative configuration
} } } }
How exactly would the algorithm for generation of this |
Beta Was this translation helpful? Give feedback.
-
Profile referencingOne idea to consider: Would it be reasonable to reference the profile by standard path convention, i.e. "role": {
"provider": "map",
"map": {
"storage": {
"provider": "jpa-map-storage",
"provider-profile": "postgres-prod"
} } } } to use rather: "role": {
"provider": "map",
"map": {
"storage": {
"provider": "jpa-map-storage/postgres-prod",
} } } } ? |
Beta Was this translation helpful? Give feedback.
-
@stianst @pedroigor @mposolda I think it would be good if you guys could weigh in on this |
Beta Was this translation helpful? Give feedback.
-
@hmlnarik @sguilhen The proposal about using profiles sounds like a good idea to solve the original problem with the From a distribution perspective, I can think of two possible configuration approaches:
I'm not 100% sure about the #1 above because the storage configuration is dynamic in the sense that users can define their own providers and name profiles accordingly, making it harder to keep it aligned with other options in terms of help message and auto-completion. Another problem with this approach is that we don't have yet discussed the outline for extensions. The #2 approach is what I think is simpler, with a good UX, and flexible enough to support all the configuration aspects. By following this approach we could have a configuration namespace For instance, if you want a single database for all areas we could still leverage the existing options to replace values in the default configuration. It should also be possible to dynamically create data sources based on the storage configuration if users define different profiles for the JPA storage. This one, in particular, should make it easier when you need to use your own storage implementation to integrate with a legacy identity store for users, roles, and groups. The same goes for a top-level LDAP configuration, to which we could provide specific options for configuring LDAP. I'm not 100% sure yet about other potential use cases and combinations that make sense to provide out of the box via de the default configuration. I'm also not 100% about how far people are going to use the top-level configuration to configure the store rather than configuring the storage on a per-realm basis. |
Beta Was this translation helpful? Give feedback.
-
This discussion aims to address the issue repored in #10226
The problem
To summarize the issue, we can have areas, such as
role
,group
,user
, etc in the system configuration that use the sameMapStorageProvider
implementation - e.g.jpa-map-storage
, but with different configurations. Currently the storage providers can define a root configuration in themapStorage
SPI, and areas that need to use a different storage configuration have to specify the storage configuration as part of their own config. Something like this:However, all the storage factories for the different areas are assigned the same
componentId
, whose format ismapStorage-f:ROOT:storage_name
and the first storage factory that is instantiated is cached under thecomponentId
key, which in the end makes all areas use the same configuration. So instead of havingrole
andclient
using different DBs they both end up in the same DB - the one specified in the configuration of the area that is requested first.So the issue is that the
componentId
does not consider the configuration of the storage provider, and we need to think about ways to fix this.Possible Solutions
Include the configuration hashcode in the
componentId
Two configs for a storage provider are equal if they yield the same values for the set of supported configuration attributes. This means that we could add a
getSupportedAttributes
method to the factories (perhaps to theAmphibianProviderFactory
) that returns the set of attributes the particular implementation supports, and then we could fetch the attribute values using theScope
that is constructed for the particular area storage and store all key/value pair in aHashMap
. The final map's hashcode can then be appended to thecomponentId
.With this approach, two different areas that have the same config end up sharing the same factory. However, this means that every time a storage is requested we will have to compute this hashcode to determine if we can reuse a previously instantiated factory. In addition, if two or more areas need to share the same alternative configuration, we still need to replicate the config for each area.
Configure the
componentId
manually for each areaIn this case, one could manually set the
componentId
for the areas, and those with the samecomponentId
end up sharing the same storage factory. Something like this:This would work but it we still need to replicate the config for all areas that want to share an alternative configuration because we don't know which area will be requested first (and thus have its storage factory initialized and cached for the others to use).
Proposal: add profiles for the storage factory configurations and have areas reference specific profiles
The idea here is to expand the configuration of the storage providers to support multiple profiles, and have each area reference one of the profiles in their own configuration. The
componentId
would be computed asmapStorage:storage_provider:profile
. For example, if we have a profile namedpostgres-dev
for thejpa-map-storage
provider, thecomponentId
would be computed asmapStorage:jpa-map-storage:postgres-dev
.The configuration of a root storage factory would look like this (NOTE: in the examples we're using the
jpa-map-storage
but the idea can be applied to any storage provider):default-profile
is optional, in the absence of this attribute we could look for a profile nameddefault
and if there is no such profile we could use the factory's root configuration as the scope that is fed toinit
. For example,Would be equivalent of a configuration that has a single profile named
default
. i.e.Now each individual area would be able to specify the profile in its configuration and reuse the same config. For example,
Would use the
postgres-dev
profile of thejpa-map-storage
provider for the roles. In the absence of theprovider-profile
, the storage factory's default profile would be used (and for that we would check what default was configured in the root provider configuration). That isWould end up using the default profile for the
jpa-map-storage
. If our config looks like the first example in this section, we would end up using thepostgres-dev
profile.Every area using the same storage provider and profile would end up having the same
componentId
and thus share the same storage provider factory. In addition, this no longer requires copying and pasting the same config for every area that needs to use a different database configuration. Finally, this solution makes it easier to switch between dev and production profiles by having all areas use the default profile and then change the default to a different profile in the root configuration.Area-specific storage provider configuration
It would still be possible to have a config specific to an area. One way to do it would be to define a separate profile that is used only by that single area. A different approach would be to have the storage config specified in the area itself, much like what we do today:
In this case, we check if there is an inline configuration for the storage and if we have one, we can generate a
componentId
just for the specific area. Something likemapStorage:jpa-map-storage:role-inline
. As a result it would have its own storage factory.The inline config would still fallback to the root configuration as we have today. In other words, if an attribute can't be found in the inline config it checks the root config. The only difference is that in this case we would fallback to one of the profiles. If the
provider-profile
isn't specified, we fallback to the default profile as determined by the root configuration. If theprovider-profile
is specified, we fallback to the specific profile.So, the example above would fallback to the
postgres-dev
profile as that is the default profile in the root configuration. So therole
config can, for example, have just a different connection URI that uses a different DB, and get all other attributes, such as user, password, etc, from thepostgres-dev
profile.The following inline config
Would use the inline config and fallback to the
postgres-prod
profile.Of course there are a few implementation-specific details that would need to be sorted out to make all this work, but it is flexible in the sense that it expands the storage factories configuration and it allows for the areas to use different storage configurations simply by referencing them instead of copying and pasting the config into each area. Plus, it can still provide a way for an area to have a very specific config by keeping the inline configuration that falls back into one of the profiles.
Thoughts, comments and other considerations are highly welcome!
Beta Was this translation helpful? Give feedback.
All reactions