1+ from time import monotonic , sleep
2+
13from django .db .backends .base .schema import BaseDatabaseSchemaEditor
24from django .db .models import Index , UniqueConstraint
35from pymongo .operations import SearchIndexModel
@@ -269,10 +271,12 @@ def add_index(
269271 )
270272 if idx :
271273 model = parent_model or model
274+ collection = self .get_collection (model ._meta .db_table )
272275 if isinstance (idx , SearchIndexModel ):
273- self .get_collection (model ._meta .db_table ).create_search_index (idx )
276+ collection .create_search_index (idx )
277+ self .wait_until_index_created (collection , index .name )
274278 else :
275- self . get_collection ( model . _meta . db_table ) .create_indexes ([idx ])
279+ collection .create_indexes ([idx ])
276280
277281 def _add_composed_index (self , model , field_names , column_prefix = "" , parent_model = None ):
278282 """Add an index on the given list of field_names."""
@@ -290,12 +294,14 @@ def _add_field_index(self, model, field, *, column_prefix=""):
290294 def remove_index (self , model , index ):
291295 if index .contains_expressions :
292296 return
297+ collection = self .get_collection (model ._meta .db_table )
293298 if isinstance (index , SearchIndex ):
294299 # Drop the index if it's supported.
295300 if self .connection .features .supports_atlas_search :
296- self .get_collection (model ._meta .db_table ).drop_search_index (index .name )
301+ collection .drop_search_index (index .name )
302+ self .wait_until_index_dropped (collection , index .name )
297303 else :
298- self . get_collection ( model . _meta . db_table ) .drop_index (index .name )
304+ collection .drop_index (index .name )
299305
300306 def _remove_composed_index (
301307 self , model , field_names , constraint_kwargs , column_prefix = "" , parent_model = None
@@ -420,6 +426,32 @@ def _field_should_have_unique(self, field):
420426 # The _id column is automatically unique.
421427 return db_type and field .unique and field .column != "_id"
422428
429+ @staticmethod
430+ def wait_until_index_created (collection , index_name , timeout = 60 * 60 , interval = 0.5 ):
431+ """
432+ Wait up to an hour until an index is created. Index creation time
433+ depends on the size of the collection being indexed.
434+ """
435+ start = monotonic ()
436+ while monotonic () - start < timeout :
437+ indexes = list (collection .list_search_indexes ())
438+ for idx in indexes :
439+ if idx ["name" ] == index_name and idx ["status" ] == "READY" :
440+ return True
441+ sleep (interval )
442+ raise TimeoutError (f"Index { index_name } not ready after { timeout } seconds." )
443+
444+ @staticmethod
445+ def wait_until_index_dropped (collection , index_name , timeout = 60 , interval = 0.5 ):
446+ """Wait up to 60 seconds until an index is dropped."""
447+ start = monotonic ()
448+ while monotonic () - start < timeout :
449+ indexes = list (collection .list_search_indexes ())
450+ if all (idx ["name" ] != index_name for idx in indexes ):
451+ return True
452+ sleep (interval )
453+ raise TimeoutError (f"Index { index_name } not dropped after { timeout } seconds." )
454+
423455
424456# GISSchemaEditor extends some SchemaEditor methods.
425457class DatabaseSchemaEditor (GISSchemaEditor , BaseSchemaEditor ):
0 commit comments