terrymanu commented on issue #30446:
URL: 
https://github.com/apache/shardingsphere/issues/30446#issuecomment-3507581375

   Problem Confirmation
   
     After thoroughly analyzing the ShardingSphere codebase and the provided 
example, I can confirm this is a legitimate design limitation in 
ShardingSphere's Shadow functionality, not a user error.
   
     Root Cause Analysis
   
     1. Architecture Design Limitation
   
     The fundamental issue lies in ShardingSphere's metadata management 
architecture for Shadow databases:
   
     - Independent Metadata Contexts: Production and shadow databases maintain 
separate metadata contexts
     - SQL Routing Only: Shadow router only handles SQL routing, not metadata 
synchronization
     - Lack of Bidirectional Sync: No automatic synchronization mechanism 
exists between production and shadow metadata
   
     2. Technical Code Analysis
   
     ShadowSQLRouter.java (line 44-62):
     public void decorateRouteContext(final RouteContext routeContext, final 
QueryContext queryContext, 
                                     final ShardingSphereDatabase database, 
final ShadowRule rule, 
                                     final Collection<String> tableNames, final 
ConfigurationProperties props) {
         // Only handles routing units, does not process metadata 
synchronization
         Map<String, String> shadowDataSourceMappings = 
ShadowDataSourceMappingsRetrieverFactory.newInstance(queryContext).retrieve(rule);
         // No DDL awareness or metadata sync logic
     }
   
     ContextManager.java (line 175-189):
     public void reloadDatabase(final ShardingSphereDatabase database) {
         MetaDataContexts reloadedMetaDataContexts = 
createMetaDataContexts(database);
         // Reloads ALL data sources' metadata, too expensive for specific 
shadow pairs
     }
   
     ShardingSphereDatabaseFactory.java (line 80-88):
     public static ShardingSphereDatabase create(final String name, final 
DatabaseType protocolType, 
                                               final DatabaseConfiguration 
databaseConfig,
                                               final ConfigurationProperties 
props, 
                                               final ComputeNodeInstanceContext 
computeNodeInstanceContext) throws SQLException {
         // Each data source independently builds metadata, no synchronization 
mechanism
         Map<String, ShardingSphereSchema> schemas = new 
ConcurrentHashMap<>(GenericSchemaBuilder.build(
             protocolType, new 
GenericSchemaBuilderMaterial(resourceMetaData.getStorageUnits(),
                                                            databaseRules, 
props, defaultSchemaName)));
     }
   
     3. The Specific Problem
   
     From your example code:
   
     // Production database - uses IF NOT EXISTS
     public void createTableIfNotExists() throws SQLException {
         String sql = "CREATE TABLE IF NOT EXISTS t_order " +
                         "(order_id BIGINT NOT NULL AUTO_INCREMENT, order_type 
INT(11), user_id INT NOT NULL, address_id BIGINT NOT NULL, status VARCHAR(50), 
PRIMARY KEY (order_id))";
         // This works on subsequent executions
     }
   
     // Shadow database - missing IF NOT EXISTS  
     public void createTableIfNotExistsShadow() throws SQLException {
         String sql = "CREATE TABLE t_order (order_id BIGINT NOT NULL 
AUTO_INCREMENT, order_type INT(11), user_id INT NOT NULL, address_id BIGINT NOT 
NULL, status VARCHAR(50), PRIMARY KEY (order_id)) /* 
     SHARDINGSPHERE_HINT:shadow=true,foo=bar*/";
         // This fails on second execution with "Table already exists"
     }
   
     4. Why This is a Design Issue
   
     Expected Behavior: Users expect Shadow functionality to handle shadow 
database state transparently without requiring manual metadata management.
   
     Current Limitation: The system should provide automatic metadata 
synchronization between production and shadow databases, especially for DDL 
operations.
   
     Solution Recommendations
   
     1. Immediate Workaround
   
     Use IF NOT EXISTS in shadow table creation:
   
     public void createTableIfNotExistsShadow() throws SQLException {
         String sql = "CREATE TABLE IF NOT EXISTS t_order (order_id BIGINT NOT 
NULL AUTO_INCREMENT, order_type INT(11), user_id INT NOT NULL, address_id 
BIGINT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_id)) /* 
     SHARDINGSPHERE_HINT:shadow=true,foo=bar*/";
         try (Connection connection = dataSource.getConnection();
              Statement statement = connection.createStatement()) {
             statement.executeUpdate(sql);
         }
     }
   
     2. Short-term Solution - DDL-Aware Shadow Router
   
     Extend ShadowSQLRouter to detect DDL and synchronize metadata:
   
     public void decorateRouteContext(final RouteContext routeContext, final 
QueryContext queryContext, 
                                     final ShardingSphereDatabase database, 
final ShadowRule rule, 
                                     final Collection<String> tableNames, final 
ConfigurationProperties props) {
   
         Map<String, String> shadowDataSourceMappings = 
ShadowDataSourceMappingsRetrieverFactory.newInstance(queryContext).retrieve(rule);
   
         // NEW: DDL detection and metadata synchronization
         if (isDDLStatement(queryContext)) {
             synchronizeMetaDataForShadowPairs(database, 
shadowDataSourceMappings, queryContext);
         }
   
         // Existing routing logic continues...
     }
   
     private boolean isDDLStatement(final QueryContext queryContext) {
         return 
queryContext.getSqlStatementContext().getSqlStatement().getClass().getName()
                 .contains("CreateTableStatement") ||
                
queryContext.getSqlStatementContext().getSqlStatement().getClass().getName()
                 .contains("DropTableStatement");
     }
   
     3. Long-term Solution - Shadow Metadata Synchronization Service
   
     Introduce dedicated service for shadow metadata management:
   
     public class ShadowMetaDataSynchronizationService {
   
         /**
          * Synchronize metadata between production and shadow data sources
          */
         public void synchronizeShadowPairMetaData(final ShardingSphereDatabase 
database,
                                                 final String 
productionDataSource,
                                                 final String shadowDataSource) 
{
             // Compare metadata differences between two data sources
             // Execute necessary synchronization operations
         }
   
         /**
          * Check metadata consistency
          */
         public boolean isMetaDataConsistent(final ShardingSphereDatabase 
database,
                                            final String productionDataSource,
                                            final String shadowDataSource) {
             // Implement metadata consistency check logic
         }
     }
   
     4. Configuration-Based Solution
   
     Allow users to configure metadata synchronization strategy:
   
     rules:
     - !SHADOW
       dataSources:
         shadow-data-source:
           productionDataSourceName: ds_production
           shadowDataSourceName: ds_shadow
           metaDataSyncStrategy: AUTO  # AUTO, MANUAL, NONE
           syncOnDDL: true
   
     Files That Need Modification
   
     Core Changes Required:
   
     1. ShadowSQLRouter.java - Add DDL awareness and metadata sync
     2. ContextManager.java - Add selective shadow data source pair reload
     3. ShadowRule.java - Add metadata synchronization configuration
     4. New File: ShadowMetaDataSynchronizationService.java
   
     Implementation Priority
   
     - High Priority: DDL-aware metadata synchronization
     - Medium Priority: Manual synchronization API
     - Low Priority: Configurable automatic sync strategy
   
     Questions for the User
   
     To help us implement the best solution:
   
     1. Execution Pattern: Does this issue occur when running the application 
multiple times?
     2. Workaround Validation: Have you tried using IF NOT EXISTS in the shadow 
creation method?
     3. Production Scenario: In your use case, should shadow tables be created 
automatically when production tables are created?
   
     Conclusion
   
     This issue reveals a genuine design limitation in ShardingSphere's Shadow 
functionality regarding metadata management. The current architecture lacks 
automatic synchronization between production and shadow database
     metadata.
   
     We recommend implementing the DDL-aware synchronization mechanism as a 
priority fix, while the IF NOT EXISTS workaround should resolve the immediate 
issue for most use cases.
   
     Thank you for bringing this important issue to our attention. This will 
significantly improve Shadow functionality's usability in production 
environments.
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to