@@ -166,7 +166,8 @@ public void start(final Map<String, String> props) {
166166    }
167167
168168    heartbeatManager  = null ;
169-     createPartitionMap (sourceConfig , true );
169+     partitionMap  = null ;
170+     createPartitionMap (sourceConfig );
170171
171172    mongoClient  =
172173        MongoClients .create (
@@ -407,17 +408,14 @@ private MongoChangeStreamCursor<? extends BsonDocument> tryCreateCursor(
407408        LOGGER .info ("Namespace not found cursor closed." );
408409      } else  {
409410        LOGGER .warn (
410-             "Failed to resume change stream: {} {}\n " 
411+             "Failed to resume change stream: {} {}\n \n " 
411412                + "=====================================================================================\n " 
412413                + "If the resume token is no longer available then there is the potential for data loss.\n " 
413414                + "Saved resume tokens are managed by Kafka and stored with the offset data.\n \n " 
414-                 + "To restarting the change stream with no resume token set `errors.tolerance=all` \n " 
415-                 + "or by manually removing the old token.\n \n " 
416-                 + "When running Connect in standalone mode offsets are configured using the:\n " 
417-                 + "`offset.storage.file.filename` configuration.\n " 
418-                 + "When running Connect in distributed mode the offsets are stored in a topic.\n \n " 
419-                 + "Use the `kafka-consumer-groups.sh` tool with the `--reset-offsets` flag to reset\n " 
420-                 + "offsets.\n \n " 
415+                 + "To restart the change stream with no resume token either: \n " 
416+                 + "  * Create a new partition name using the `offset.partition.name` configuration.\n " 
417+                 + "  * Set `errors.tolerance=all` and ignore the erroring resume token. \n " 
418+                 + "  * Manually remove the old offset from its configured storage.\n \n " 
421419                + "Resetting the offset will allow for the connector to be resume from the latest resume\n " 
422420                + "token. Using `copy.existing=true` ensures that all data will be outputted by the\n " 
423421                + "connector but it will duplicate existing data.\n " 
@@ -462,41 +460,40 @@ String getTopicNameFromNamespace(final String prefix, final BsonDocument namespa
462460  }
463461
464462  Map <String , Object > createPartitionMap (final  MongoSourceConfig  sourceConfig ) {
465-     return  createPartitionMap (sourceConfig , partitionMap  == null );
466-   }
467- 
468-   private  Map <String , Object > createPartitionMap (
469-       final  MongoSourceConfig  sourceConfig , final  boolean  recreate ) {
470-     if  (recreate ) {
471-       partitionMap  = singletonMap (NS_KEY , createNamespaceString (sourceConfig , false ));
463+     if  (partitionMap  == null ) {
464+       String  partitionName  = sourceConfig .getString (MongoSourceConfig .OFFSET_PARTITION_NAME_CONFIG );
465+       if  (partitionName .isEmpty ()) {
466+         partitionName  = createDefaultPartitionName (sourceConfig );
467+       }
468+       partitionMap  = singletonMap (NS_KEY , partitionName );
472469    }
473470    return  partitionMap ;
474471  }
475472
476473  Map <String , Object > createLegacyPartitionMap (final  MongoSourceConfig  sourceConfig ) {
477-     return  singletonMap (NS_KEY , createNamespaceString (sourceConfig ,  true ));
474+     return  singletonMap (NS_KEY , createLegacyPartitionName (sourceConfig ));
478475  }
479476
480-   String  createNamespaceString (final  MongoSourceConfig  sourceConfig , final  boolean  legacy ) {
481-     if  (legacy ) {
482-       return  format (
483-           "%s/%s.%s" ,
484-           sourceConfig .getString (CONNECTION_URI_CONFIG ),
485-           sourceConfig .getString (DATABASE_CONFIG ),
486-           sourceConfig .getString (COLLECTION_CONFIG ));
487-     } else  {
488-       ConnectionString  connectionString  = sourceConfig .getConnectionString ();
489-       StringBuilder  builder  = new  StringBuilder ();
490-       builder .append (connectionString .isSrvProtocol () ? "mongodb+srv://"  : "mongodb://" );
491-       builder .append (String .join ("," , connectionString .getHosts ()));
492-       builder .append ("/" );
493-       builder .append (sourceConfig .getString (DATABASE_CONFIG ));
494-       if  (!sourceConfig .getString (COLLECTION_CONFIG ).isEmpty ()) {
495-         builder .append ("." );
496-         builder .append (sourceConfig .getString (COLLECTION_CONFIG ));
497-       }
498-       return  builder .toString ();
477+   String  createLegacyPartitionName (final  MongoSourceConfig  sourceConfig ) {
478+     return  format (
479+         "%s/%s.%s" ,
480+         sourceConfig .getString (CONNECTION_URI_CONFIG ),
481+         sourceConfig .getString (DATABASE_CONFIG ),
482+         sourceConfig .getString (COLLECTION_CONFIG ));
483+   }
484+ 
485+   String  createDefaultPartitionName (final  MongoSourceConfig  sourceConfig ) {
486+     ConnectionString  connectionString  = sourceConfig .getConnectionString ();
487+     StringBuilder  builder  = new  StringBuilder ();
488+     builder .append (connectionString .isSrvProtocol () ? "mongodb+srv://"  : "mongodb://" );
489+     builder .append (String .join ("," , connectionString .getHosts ()));
490+     builder .append ("/" );
491+     builder .append (sourceConfig .getString (DATABASE_CONFIG ));
492+     if  (!sourceConfig .getString (COLLECTION_CONFIG ).isEmpty ()) {
493+       builder .append ("." );
494+       builder .append (sourceConfig .getString (COLLECTION_CONFIG ));
499495    }
496+     return  builder .toString ();
500497  }
501498
502499  /** 
@@ -642,7 +639,8 @@ Map<String, Object> getOffset(final MongoSourceConfig sourceConfig) {
642639    if  (context  != null ) {
643640      Map <String , Object > offset  =
644641          context .offsetStorageReader ().offset (createPartitionMap (sourceConfig ));
645-       if  (offset  == null ) {
642+       if  (offset  == null 
643+           && sourceConfig .getString (MongoSourceConfig .OFFSET_PARTITION_NAME_CONFIG ).isEmpty ()) {
646644        offset  = context .offsetStorageReader ().offset (createLegacyPartitionMap (sourceConfig ));
647645      }
648646      return  offset ;
@@ -659,7 +657,7 @@ BsonDocument getResumeToken(final MongoSourceConfig sourceConfig) {
659657      invalidatedCursor  = false ;
660658    } else  {
661659      Map <String , Object > offset  = getOffset (sourceConfig );
662-       if  (offset  != null  && !offset .containsKey (COPY_KEY )) {
660+       if  (offset  != null  && offset . containsKey ( ID_FIELD ) &&  !offset .containsKey (COPY_KEY )) {
663661        resumeToken  = BsonDocument .parse ((String ) offset .get (ID_FIELD ));
664662        if  (offset .containsKey (HEARTBEAT_KEY )) {
665663          LOGGER .info ("Resume token from heartbeat: {}" , resumeToken );
0 commit comments