grails / grails-database-migration

Grails® framework Database Migration Plugin
Apache License 2.0
98 stars 116 forks source link

Migration with Multitenant (DATABASE) version:5.0.0-SNAPSHOT #380

Open pinedu1 opened 2 months ago

pinedu1 commented 2 months ago

Hello! When activating a MULTITENANT (DATABASE) project, where the configuration is as follows:

SO:

Where I downloaded the source code from the 5.x branch, compiled it, and distributed it to my local Maven repository: /code/

> ./gradlew publishToMavenLocal -x groovydoc

/code/

I added this compilation to my project: build.gradle /code/ buildscript { dependencies { classpath 'org.grails.plugins:database-migration:5.0.0-SNAPSHOT' } } sourceSets { main { resources { srcDir 'grails-app/migrations' } } } ... dependencies { ... implementation('org.grails.plugins:database-migration:5.0.0-SNAPSHOT') } /code/

tenantresolver: org.grails.datastore.mapping.multitenancy.TenantResolver (SUBDOMAIN) application.properties:

Configurações da Aplicação

grails.gorm.reactor.events=false grails.gorm.multiTenancy.mode=DATABASE grails.gorm.multiTenancy.tenantResolverClass=org.grails.datastore.mapping.multitenancy.TenantResolver (SUBDOMAIN) grails.plugin.databasemigration.updateAllOnStart=true grails.plugin.databasemigration.updateOnStartFileName=changelog.groovy grails.plugin.databasemigration.changelogFileName=changelog.groovy

Default

dataSource.url=jdbc:postgresql://localhost:5432/pnd dataSource.driverClassName=org.postgresql.Driver dataSource.username=pnd dataSource.password=pnd dataSource.pooled=true dataSource.jmxExport=true dataSource.dbCreate=none

Cliente: joao

dataSources.JOAO.dbCreate=none dataSources.JOAO.password=joao dataSources.JOAO.url=jdbc:postgresql://localhost:5432/joao dataSources.JOAO.username=joao

Cliente: pedro

dataSources.PEDRO.dbCreate=none dataSources.PEDRO.password=pedro dataSources.PEDRO.url=jdbc:postgresql://localhost:5432/pedro dataSources.PEDRO.username=pedro

Cliente: antonio

dataSources.ANTONIO.dbCreate=none dataSources.ANTONIO.password=antonio dataSources.ANTONIO.url=jdbc:postgresql://localhost:5432/antonio dataSources.ANTONIO.username=antonio

Cliente: jose

dataSources.JOSE.dbCreate=none dataSources.JOSE.password=jose dataSources.JOSE.url=jdbc:postgresql://localhost:5432/jose dataSources.JOSE.username=jose

It turns out that in this configuration, the MULTITENANT setup creates the DATASOURCE_NAME map as follows: datasources.JOAO=[map of properties]
datasources.PEDRO=[map of properties]
...
datasources.ANTONIO=[map of properties]

These dataSources are collected by the plugin in: class: DatabaseMigrationGrailsPlugin

And in line 100, there is the following method: / code / private Set getDataSourceNames() { def dataSources = config.getProperty('dataSources', Map, [:]) if (!dataSources) { return ['dataSource'] } Set dataSourceNames = dataSources.keySet() if (!dataSourceNames.contains('dataSource')) { dataSourceNames = ['dataSource'] + dataSourceNames } return dataSourceNames } / code /

In this method, the if block: /code/ if (!dataSourceNames.contains('dataSource')) { dataSourceNames = ['dataSource'] + dataSourceNames } /code/ cannot exist in this configuration, as it creates a false entry in the dataSources set and breaks the UPDATE_ALL_ON_START functionality. In my solution, I simply removed this piece of code (the if condition above).

A second point related to my configuration:

The class DatabaseMigrationTransactionManager also collects the databases to retrieve the TransactionManager linked to each of these tenants. In line 27 of this class: /code/ PlatformTransactionManager getTransactionManager() { String dataSource = this.dataSource ?: "dataSource" String beanName = "transactionManager" if (dataSource != "dataSource") { beanName += "_${dataSource}" } applicationContext.getBean(beanName, PlatformTransactionManager) } /code/ The method retrieves the transaction managers, and in my configuration, it retrieves by the object's name: "transactionManager_JOAO" "transactionManager_PEDRO" ... "transactionManager_ANTONIO"

However, at some point, the dataSource name becomes degenerated into the following form:

"dataSource_JOAO" "dataSource_PEDRO" ... "dataSource_ANTONIO"

Where it should simply be:

"JOAO" "PEDRO" ... "ANTONIO"

My homemade solution was to modify the method as follows:

/code/ PlatformTransactionManager getTransactionManager() { String dataSource = this.dataSource ?: "dataSource" String beanName = "transactionManager" if (dataSource.startsWith('dataSource')) { dataSource = dataSource.substring(dataSource.indexOf('') + 1) } if (dataSource != "dataSource") { beanName += "_${dataSource}" } applicationContext.getBean(beanName, PlatformTransactionManager) } /code/

Note that I force the elimination of the 'dataSource' substring in the following if block: /code/ if (dataSource.startsWith('dataSource')) { dataSource = dataSource.substring(dataSource.indexOf('_') + 1) } /code/