@@ -545,21 +545,72 @@ def create_unique_indexes_batch(collection, keys):
545545 """
546546 Create multiple unique indexes at once.
547547 Required for Cosmos DB which mandates unique indexes at collection creation.
548+ If unique creation fails due to Cosmos DB restriction, fallback to non-unique indexes.
548549 """
549550 try :
550551 indexes = [IndexModel ([(key , ASCENDING )], unique = True ) for key in keys ]
551552 collection .create_indexes (indexes )
552553 except Exception as e :
553554 # Check for Cosmos DB "unique index cannot be modified" error (Code 13)
554- if hasattr (e , 'code' ) and e .code == 13 :
555+ if ( hasattr (e , 'code' ) and e .code == 13 ) or "The unique index cannot be modified" in str ( e ) :
555556 logging .getLogger (__name__ ).warning (
556- f"Skipped unique indexes on { collection .name } ({ keys } ) due to Cosmos DB restriction. "
557- "Unique constraint may not be enforced if collection was created without it. "
558- "To fix: Delete collection '{collection.name}' in Azure Portal and restart."
557+ f"Cosmos DB restriction: Cannot create unique indexes on { collection .name } ({ keys } ) because collection already exists. "
558+ "Falling back to non-unique indexes to ensure performance."
559559 )
560+ try :
561+ # Fallback: Create regular indexes so queries are still fast
562+ indexes = [IndexModel ([(key , ASCENDING )], unique = False ) for key in keys ]
563+ collection .create_indexes (indexes )
564+ logging .getLogger (__name__ ).info (f"Successfully created fallback non-unique indexes on { collection .name } " )
565+ except Exception as fallback_error :
566+ logging .getLogger (__name__ ).warning (f"Failed to create fallback indexes on { collection .name } : { fallback_error } " )
560567 else :
561568 logging .getLogger (__name__ ).warning (f"Failed to create unique indexes on { collection .name } : { e } " )
562569
570+ # Helper to safely create a single unique index with robust fallback
571+ def create_index_with_fallback (collection , keys , unique = True , ** kwargs ):
572+ """
573+ Safely create an index.
574+ 1. Try to create with desired options (e.g. unique=True).
575+ 2. If 'already exists with different options', drop and retry.
576+ 3. If Cosmos DB error (Code 13), fallback to unique=False.
577+ """
578+ try :
579+ collection .create_index (keys , unique = unique , ** kwargs )
580+ except Exception as e :
581+ error_msg = str (e )
582+
583+ # Case 1: Index exists with different options -> Drop and Retry
584+ if "already exists with different options" in error_msg :
585+ try :
586+ logging .getLogger (__name__ ).warning (f"Index conflict on { collection .name } . Dropping and recreating..." )
587+ # PyMongo allows dropping by key specification
588+ collection .drop_index (keys )
589+ # Retry creation (recursive call to handle potential Code 13 on retry)
590+ create_index_with_fallback (collection , keys , unique = unique , ** kwargs )
591+ return
592+ except Exception as drop_error :
593+ logging .getLogger (__name__ ).warning (f"Failed to drop/recreate index on { collection .name } : { drop_error } " )
594+ # If drop failed, we can't do much, but maybe we can try fallback if it was a uniqueness issue?
595+ # Unlikely if drop failed.
596+ return
597+
598+ # Case 2: Cosmos DB "unique index cannot be modified" (Code 13)
599+ if unique and ((hasattr (e , 'code' ) and e .code == 13 ) or "The unique index cannot be modified" in error_msg ):
600+ logging .getLogger (__name__ ).warning (
601+ f"Cosmos DB restriction: Cannot create unique index on { collection .name } . "
602+ "Falling back to non-unique index."
603+ )
604+ try :
605+ collection .create_index (keys , unique = False , ** kwargs )
606+ logging .getLogger (__name__ ).info (f"Successfully created fallback non-unique index on { collection .name } " )
607+ except Exception as fallback_error :
608+ logging .getLogger (__name__ ).warning (f"Failed to create fallback index on { collection .name } : { fallback_error } " )
609+ return
610+
611+ # Other errors
612+ logging .getLogger (__name__ ).warning (f"Failed to create index on { collection .name } : { e } " )
613+
563614 # Users collection indexes - Batch creation for unique constraints
564615 create_unique_indexes_batch (db .users , ["username" , "email" ])
565616
@@ -658,80 +709,49 @@ def create_unique_indexes_batch(collection, keys):
658709 db .tickets .create_index ("reviewed_by" )
659710
660711 # Conversation settings collection indexes (Upgrade to partial indexes)
661- try :
662- db .conversation_settings .create_index (
663- [("user_id" , 1 ), ("other_user_id" , 1 )],
664- unique = True ,
665- partialFilterExpression = {"other_user_id" : {"$exists" : True }}
666- )
667- except Exception as e :
668- if "already exists with different options" in str (e ):
669- logger .warning ("Index conflict detected. Dropping old index: user_id_1_other_user_id_1" )
670- db .conversation_settings .drop_index ("user_id_1_other_user_id_1" )
671- db .conversation_settings .create_index (
672- [("user_id" , 1 ), ("other_user_id" , 1 )],
673- unique = True ,
674- partialFilterExpression = {"other_user_id" : {"$exists" : True }}
675- )
676- else :
677- logger .warning (f"Index creation failed for user_id_1_other_user_id_1: { e } " )
678-
679- try :
680- db .conversation_settings .create_index (
681- [("user_id" , 1 ), ("topic_id" , 1 ), ("type" , 1 )],
682- unique = True ,
683- partialFilterExpression = {"topic_id" : {"$exists" : True }}
684- )
685- except Exception as e :
686- if "already exists with different options" in str (e ):
687- logger .warning ("Index conflict detected. Dropping old index: user_id_1_topic_id_1_type_1" )
688- db .conversation_settings .drop_index ("user_id_1_topic_id_1_type_1" )
689- db .conversation_settings .create_index (
690- [("user_id" , 1 ), ("topic_id" , 1 ), ("type" , 1 )],
691- unique = True ,
692- partialFilterExpression = {"topic_id" : {"$exists" : True }}
693- )
694- else :
695- logger .warning (f"Index creation failed for user_id_1_topic_id_1_type_1: { e } " )
696-
697- try :
698- db .conversation_settings .create_index (
699- [("user_id" , 1 ), ("chat_room_id" , 1 ), ("type" , 1 )],
700- unique = True ,
701- partialFilterExpression = {"chat_room_id" : {"$exists" : True }}
702- )
703- except Exception as e :
704- if "already exists with different options" in str (e ):
705- logger .warning ("Index conflict detected. Dropping old index: user_id_1_chat_room_id_1_type_1" )
706- db .conversation_settings .drop_index ("user_id_1_chat_room_id_1_type_1" )
707- db .conversation_settings .create_index (
708- [("user_id" , 1 ), ("chat_room_id" , 1 ), ("type" , 1 )],
709- unique = True ,
710- partialFilterExpression = {"chat_room_id" : {"$exists" : True }}
711- )
712- else :
713- logger .warning (f"Index creation failed for user_id_1_chat_room_id_1_type_1: { e } " )
712+ create_index_with_fallback (
713+ db .conversation_settings ,
714+ [("user_id" , 1 ), ("other_user_id" , 1 )],
715+ unique = True ,
716+ partialFilterExpression = {"other_user_id" : {"$exists" : True }}
717+ )
718+ create_index_with_fallback (
719+ db .conversation_settings ,
720+ [("user_id" , 1 ), ("topic_id" , 1 ), ("type" , 1 )],
721+ unique = True ,
722+ partialFilterExpression = {"topic_id" : {"$exists" : True }}
723+ )
724+ create_index_with_fallback (
725+ db .conversation_settings ,
726+ [("user_id" , 1 ), ("chat_room_id" , 1 ), ("type" , 1 )],
727+ unique = True ,
728+ partialFilterExpression = {"chat_room_id" : {"$exists" : True }}
729+ )
714730 db .conversation_settings .create_index ("type" )
715731
716732 # Notification settings collection indexes
717- db .notification_settings .create_index (
733+ create_index_with_fallback (
734+ db .notification_settings ,
718735 [("user_id" , 1 ), ("post_id" , 1 ), ("type" , 1 )],
719- unique = True ,
736+ unique = True ,
720737 partialFilterExpression = {"type" : "post" }
721738 )
722- db .notification_settings .create_index (
739+ create_index_with_fallback (
740+ db .notification_settings ,
723741 [("user_id" , 1 ), ("chat_room_id" , 1 ), ("type" , 1 )],
724- unique = True ,
742+ unique = True ,
725743 partialFilterExpression = {"type" : "chat_room" }
726744 )
727- db .notification_settings .create_index (
745+ create_index_with_fallback (
746+ db .notification_settings ,
728747 [("user_id" , 1 ), ("topic_id" , 1 ), ("type" , 1 )],
729- unique = True ,
748+ unique = True ,
730749 partialFilterExpression = {"type" : "topic" }
731750 )
732- db .notification_settings .create_index (
751+ create_index_with_fallback (
752+ db .notification_settings ,
733753 [("user_id" , 1 ), ("other_user_id" , 1 ), ("type" , 1 )],
734- unique = True ,
754+ unique = True ,
735755 partialFilterExpression = {"type" : "private_message" }
736756 )
737757 db .notification_settings .create_index ("type" )
@@ -742,7 +762,11 @@ def create_unique_indexes_batch(collection, keys):
742762 db .private_messages .create_index ("created_at" )
743763
744764 # Anonymous identities collection indexes
745- db .anonymous_identities .create_index ([("user_id" , 1 ), ("topic_id" , 1 )], unique = True )
765+ create_index_with_fallback (
766+ db .anonymous_identities ,
767+ [("user_id" , 1 ), ("topic_id" , 1 )],
768+ unique = True
769+ )
746770
747771 # Friends collection indexes (for Azure Cosmos DB compatibility)
748772 try :
@@ -761,7 +785,7 @@ def create_unique_indexes_batch(collection, keys):
761785 logger .warning (f"Failed to create some comments indexes (may already exist): { e } " )
762786
763787 # Short Links collection
764- db .short_links . create_index ( "code" , unique = True )
788+ create_index_with_fallback ( db .short_links , "code" , unique = True )
765789 db .short_links .create_index ("created_at" )
766790
767791 logger .info ("Database indexes created successfully" )
0 commit comments