@@ -270,10 +270,17 @@ def _set_model_version(
270
270
# Set model version if not defined
271
271
if model_instance ._version is None :
272
272
current_highest_version = 0
273
- for item in self ._dataset_api .list (dataset_model_path , sort_by = "NAME:desc" )[
274
- "items"
275
- ]:
273
+ files = []
274
+ offset = 0
275
+ limit = 1000
276
+ items = self ._dataset_api .list (dataset_model_path , sort_by = "NAME:desc" , offset = offset , limit = limit )["items" ]
277
+ while items :
278
+ files = files + items
279
+ offset += limit
280
+ items = self ._dataset_api .list (dataset_model_path , sort_by = "NAME:desc" , offset = offset , limit = limit )["items" ]
281
+ for item in files :
276
282
_ , file_name = os .path .split (item ["attributes" ]["path" ])
283
+ # Get highest version folder
277
284
try :
278
285
try :
279
286
current_version = int (file_name )
@@ -283,20 +290,47 @@ def _set_model_version(
283
290
current_highest_version = current_version
284
291
except RestAPIError :
285
292
pass
286
- model_instance ._version = current_highest_version + 1
287
-
288
- elif self ._dataset_api .path_exists (
289
- dataset_models_root_path
290
- + "/"
291
- + model_instance ._name
292
- + "/"
293
- + str (model_instance ._version )
294
- ):
295
- raise ModelRegistryException (
296
- "Model with name {} and version {} already exists" .format (
297
- model_instance ._name , model_instance ._version
293
+
294
+ current_highest_version += 1
295
+
296
+ # Get highest available model metadata version
297
+ # This makes sure we skip corrupt versions where the model folder is deleted manually but the backend metadata is still there
298
+ model = self ._model_api .get (model_instance ._name , current_highest_version , model_instance .model_registry_id )
299
+ while model :
300
+ current_highest_version += 1
301
+ model = self ._model_api .get (model_instance ._name , current_highest_version , model_instance .model_registry_id )
302
+
303
+ model_instance ._version = current_highest_version
304
+ else :
305
+ model_backend_object_exists = self ._model_api .get (model_instance ._name , model_instance ._version , model_instance .model_registry_id ) is not None
306
+ model_version_folder_exists = self ._dataset_api .path_exists (
307
+ dataset_models_root_path
308
+ + "/"
309
+ + model_instance ._name
310
+ + "/"
311
+ + str (model_instance ._version )
312
+ )
313
+
314
+ # Perform validations to handle possible inconsistency between db and filesystem
315
+ if model_backend_object_exists and not model_version_folder_exists :
316
+ raise ModelRegistryException (
317
+ "Model with name {0} and version {1} looks to be corrupt as the version is registered but there is no Models/{0}/{1} folder in the filesystem. Delete this version using Model.delete() or the UI and try to export this version again." .format (
318
+ model_instance ._name , model_instance ._version
319
+ )
298
320
)
299
- )
321
+ elif not model_backend_object_exists and model_version_folder_exists :
322
+ raise ModelRegistryException (
323
+ "Model with name {0} and version {1} looks to be corrupt as the version exists in the filesystem but it is not registered. To proceed, please delete the Models/{0}/{1} folder manually and try to save this model again." .format (
324
+ model_instance ._name , model_instance ._version
325
+ )
326
+ )
327
+ elif model_backend_object_exists and model_version_folder_exists :
328
+ raise ModelRegistryException (
329
+ "Model with name {} and version {} already exists, please select another version." .format (
330
+ model_instance ._name , model_instance ._version
331
+ )
332
+ )
333
+
300
334
return model_instance
301
335
302
336
def _build_resource_path (self , model_instance , artifact ):
0 commit comments