@@ -267,7 +267,7 @@ CreateAgentModelListWithLoadAction(
267267// Return the latest modification time in ns for all files/folders starting
268268// from the path. The modification time will be 0 if there is an error.
269269int64_t
270- GetModifiedTime (const std::string& path)
270+ GetPathModifiedTime (const std::string& path)
271271{
272272 // If there is an error in any step the fall-back default
273273 // modification time is 0. This means that in error cases 'path'
@@ -306,84 +306,173 @@ GetModifiedTime(const std::string& path)
306306
307307 for (const auto & child : contents) {
308308 const auto full_path = JoinPath ({path, child});
309- mtime = std::max (mtime, GetModifiedTime (full_path));
309+ mtime = std::max (mtime, GetPathModifiedTime (full_path));
310310 }
311311
312312 return mtime;
313313}
314- // Return the latest modification time in ns for '<config.pbtxt, model files>'
315- // in a model directory path. The time for "config.pbtxt" will be 0 if not
316- // found at "[model_dir_path]/config.pbtxt" or "[model_dir_path]/configs/
317- // <custom-config-name>.pbtxt" if "--model-config-name" is set. The time for
318- // "model files" includes the time for 'model_dir_path'.
319- std::pair<int64_t , int64_t >
320- GetDetailedModifiedTime (
314+
315+ } // namespace
316+
317+ ModelTimestamp::ModelTimestamp (
321318 const std::string& model_dir_path, const std::string& model_config_path)
322319{
323- // Check if 'model_dir_path' is a directory.
320+ // DLIS-7221: Raise an exception when failed to create timestamp
321+ bool init_success =
322+ ModelDirectoryPathIsValid (model_dir_path) &&
323+ ReadModelDirectoryTimestamp (model_dir_path, model_config_path) &&
324+ ReadModelDirectoryContentTimestamps (model_dir_path, model_config_path);
325+ if (!init_success) {
326+ // Same as calling the default constructor. All timestamps are presumed to
327+ // be 0 when comparing.
328+ model_timestamps_.clear ();
329+ model_config_content_name_.clear ();
330+ }
331+ }
332+
333+ bool
334+ ModelTimestamp::ModelDirectoryPathIsValid (const std::string& path) const
335+ {
324336 bool is_dir;
325- Status status = IsDirectory (model_dir_path , &is_dir);
337+ Status status = IsDirectory (path , &is_dir);
326338 if (!status.IsOk ()) {
327- LOG_ERROR << " Failed to determine modification time for '" << model_dir_path
339+ LOG_ERROR << " Failed to determine modification time for '" << path
328340 << " ': " << status.AsString ();
329- return std::make_pair ( 0 , 0 ) ;
341+ return false ;
330342 }
331343 if (!is_dir) {
332- LOG_ERROR << " Failed to determine modification time for '" << model_dir_path
344+ LOG_ERROR << " Failed to determine modification time for '" << path
333345 << " ': Model directory path is not a directory" ;
334- return std::make_pair ( 0 , 0 ) ;
346+ return false ;
335347 }
348+ return true ;
349+ }
336350
337- std::pair<int64_t , int64_t > mtime (0 , 0 ); // <config.pbtxt, model files>
338-
339- // Populate 'model files' time to the model directory time
340- status = FileModificationTime (model_dir_path, &mtime.second );
351+ bool
352+ ModelTimestamp::ReadModelDirectoryTimestamp (
353+ const std::string& model_dir_path, const std::string& model_config_path)
354+ {
355+ int64_t model_dir_time = 0 ;
356+ Status status = FileModificationTime (model_dir_path, &model_dir_time);
341357 if (!status.IsOk ()) {
342358 LOG_ERROR << " Failed to determine modification time for '" << model_dir_path
343359 << " ': " << status.AsString ();
344- return std::make_pair ( 0 , 0 ) ;
360+ return false ;
345361 }
362+ model_timestamps_.emplace (" " , model_dir_time);
346363
347- // Get files/folders in model_dir_path.
348- std::set<std::string> contents;
349- status = GetDirectoryContents (model_dir_path, &contents);
364+ return true ;
365+ }
366+
367+ bool
368+ ModelTimestamp::ReadModelDirectoryContentTimestamps (
369+ const std::string& model_dir_path, const std::string& model_config_path)
370+ {
371+ std::set<std::string> dir_contents;
372+ Status status = GetDirectoryContents (model_dir_path, &dir_contents);
350373 if (!status.IsOk ()) {
351374 LOG_ERROR << " Failed to determine modification time for '" << model_dir_path
352375 << " ': " << status.AsString ();
353- return std::make_pair ( 0 , 0 ) ;
354- }
355- // Get latest modification time for each files/folders, and place it at the
356- // correct category.
357- for ( const auto & child : contents) {
358- const auto full_path = JoinPath ({model_dir_path, child});
359- if (full_path == model_config_path ) {
360- // config.pbtxt or customized config file in configs folder
361- mtime. first = GetModifiedTime (full_path) ;
362- } else {
363- // model files
364- mtime. second = std::max (mtime. second , GetModifiedTime (full_path)) ;
376+ return false ;
377+ }
378+ for ( const auto & content_name : dir_contents) {
379+ const auto content_path = JoinPath ({model_dir_path, content_name});
380+ bool is_model_config = model_config_path. rfind (content_path, 0 ) == 0 ;
381+ if (is_model_config) {
382+ if (!model_config_content_name_. empty () ) {
383+ LOG_ERROR << " Failed to determine modification time for ' "
384+ << model_dir_path << " ': Duplicate model config is detected " ;
385+ return false ;
386+ }
387+ model_config_content_name_ = content_name ;
365388 }
389+ model_timestamps_.emplace (content_name, GetPathModifiedTime (content_path));
366390 }
367391
368- return mtime ;
392+ return true ;
369393}
370- // Return true if any file in the 'model_dir_path' has been modified more
371- // recently than 'last_ns', which represents last modified time for
372- // '<config.pbtxt, model files>'. Update the 'last_ns' to the most-recent
373- // modified time.
394+
374395bool
375- IsModified (
376- const std::string& model_dir_path, const std::string& model_config_path,
377- std::pair<int64_t , int64_t >* last_ns)
396+ ModelTimestamp::IsModified (const ModelTimestamp& new_timestamp) const
378397{
379- auto new_ns = GetDetailedModifiedTime (model_dir_path, model_config_path);
380- bool modified = std::max (new_ns.first , new_ns.second ) >
381- std::max (last_ns->first , last_ns->second );
382- last_ns->swap (new_ns);
383- return modified;
398+ int64_t old_modified_time = GetModifiedTime ();
399+ int64_t new_modified_time = new_timestamp.GetModifiedTime ();
400+ return new_modified_time > old_modified_time;
384401}
385402
386- } // namespace
403+ bool
404+ ModelTimestamp::IsModelVersionModified (
405+ const ModelTimestamp& new_timestamp, const int64_t version) const
406+ {
407+ int64_t old_modified_time = std::max (
408+ GetModelVersionModifiedTime (version),
409+ GetNonModelConfigNorVersionNorDirModifiedTime ());
410+ int64_t new_modified_time = std::max (
411+ new_timestamp.GetModelVersionModifiedTime (version),
412+ new_timestamp.GetNonModelConfigNorVersionNorDirModifiedTime ());
413+ return new_modified_time > old_modified_time;
414+ }
415+
416+ int64_t
417+ ModelTimestamp::GetModifiedTime () const
418+ {
419+ int64_t modified_time = 0 ;
420+ for (const auto & pair : model_timestamps_) {
421+ const int64_t time = pair.second ;
422+ modified_time = std::max (modified_time, time);
423+ }
424+ return modified_time;
425+ }
426+
427+ int64_t
428+ ModelTimestamp::GetModelVersionModifiedTime (const int64_t version) const
429+ {
430+ int64_t modified_time = 0 ;
431+ auto itr = model_timestamps_.find (std::to_string (version));
432+ if (itr != model_timestamps_.end ()) {
433+ modified_time = itr->second ;
434+ }
435+ return modified_time;
436+ }
437+
438+ int64_t
439+ ModelTimestamp::GetNonModelConfigNorVersionNorDirModifiedTime () const
440+ {
441+ // Get modified time excluding time from model config, model version
442+ // directory(s) and model directory.
443+ int64_t modified_time = 0 ;
444+ for (const auto & pair : model_timestamps_) {
445+ const std::string& content_name = pair.first ;
446+ bool found_non_digit_in_content_name = false ;
447+ for (const char & c : content_name) {
448+ if (std::isdigit (c) == 0 ) {
449+ found_non_digit_in_content_name = true ;
450+ break ;
451+ }
452+ }
453+ // All model version directory(s) will not 'found_non_digit_in_content_name'
454+ // as they are all digit(s).
455+ // Model directory name will not 'found_non_digit_in_content_name' as it is
456+ // empty.
457+ if (found_non_digit_in_content_name &&
458+ content_name != model_config_content_name_) {
459+ const int64_t time = pair.second ;
460+ modified_time = std::max (modified_time, time);
461+ }
462+ }
463+ return modified_time;
464+ }
465+
466+ void
467+ ModelTimestamp::SetModelConfigModifiedTime (const int64_t time_ns)
468+ {
469+ if (model_config_content_name_.empty ()) {
470+ LOG_ERROR << " Failed to set config modification time: "
471+ " model_config_content_name_ is empty" ;
472+ return ;
473+ }
474+ model_timestamps_[model_config_content_name_] = time_ns;
475+ }
387476
388477ModelRepositoryManager::ModelRepositoryManager (
389478 const std::set<std::string>& repository_paths, const bool autofill,
@@ -624,7 +713,7 @@ ModelRepositoryManager::LoadModelByDependency(
624713 auto status = model_life_cycle_->AsyncLoad (
625714 valid_model->model_id_ , itr->second ->model_path_ ,
626715 valid_model->model_config_ , itr->second ->is_config_provided_ ,
627- itr->second ->mtime_nsec_ . second > itr->second ->prev_mtime_ns_ . second ,
716+ itr->second ->prev_mtime_ns_ , itr->second ->mtime_nsec_ ,
628717 itr->second ->agent_model_list_ , [model_state](Status load_status) {
629718 model_state->status_ = load_status;
630719 model_state->ready_ .set_value ();
@@ -1406,7 +1495,7 @@ ModelRepositoryManager::InitializeModelInfo(
14061495 if (iitr != infos_.end ()) {
14071496 linfo->prev_mtime_ns_ = iitr->second ->mtime_nsec_ ;
14081497 } else {
1409- linfo->prev_mtime_ns_ = std::make_pair ( 0 , 0 );
1498+ linfo->prev_mtime_ns_ = ModelTimestamp ( );
14101499 }
14111500
14121501 // Set 'mtime_nsec_' and override 'model_path_' if current path is empty
@@ -1435,7 +1524,7 @@ ModelRepositoryManager::InitializeModelInfo(
14351524 // For file override, set 'mtime_nsec_' to minimum value so that
14361525 // the next load without override will trigger re-load to undo
14371526 // the override while the local files may still be unchanged.
1438- linfo->mtime_nsec_ = std::make_pair ( 0 , 0 );
1527+ linfo->mtime_nsec_ = ModelTimestamp ( );
14391528 linfo->model_path_ = location;
14401529 linfo->model_config_path_ = JoinPath ({location, kModelConfigPbTxt });
14411530 linfo->agent_model_list_ .reset (new TritonRepoAgentModelList ());
@@ -1445,14 +1534,16 @@ ModelRepositoryManager::InitializeModelInfo(
14451534 GetModelConfigFullPath (linfo->model_path_ , model_config_name_);
14461535 // Model is not loaded.
14471536 if (iitr == infos_.end ()) {
1448- linfo->mtime_nsec_ = GetDetailedModifiedTime (
1449- linfo->model_path_ , linfo->model_config_path_ );
1537+ linfo->mtime_nsec_ =
1538+ ModelTimestamp ( linfo->model_path_ , linfo->model_config_path_ );
14501539 } else {
14511540 // Check the current timestamps to determine if model actually has been
14521541 // modified
14531542 linfo->mtime_nsec_ = linfo->prev_mtime_ns_ ;
1454- unmodified = !IsModified (
1455- linfo->model_path_ , linfo->model_config_path_ , &linfo->mtime_nsec_ );
1543+ ModelTimestamp new_mtime_ns =
1544+ ModelTimestamp (linfo->model_path_ , linfo->model_config_path_ );
1545+ unmodified = !linfo->mtime_nsec_ .IsModified (new_mtime_ns);
1546+ linfo->mtime_nsec_ = new_mtime_ns;
14561547 }
14571548 }
14581549
@@ -1467,9 +1558,9 @@ ModelRepositoryManager::InitializeModelInfo(
14671558 // the override while the local files may still be unchanged.
14681559 auto time_since_epoch =
14691560 std::chrono::system_clock::now ().time_since_epoch ();
1470- linfo->mtime_nsec_ .first =
1561+ linfo->mtime_nsec_ .SetModelConfigModifiedTime (
14711562 std::chrono::duration_cast<std::chrono::nanoseconds>(time_since_epoch)
1472- .count ();
1563+ .count ()) ;
14731564 unmodified = false ;
14741565
14751566 const std::string& override_config = override_parameter->ValueString ();
0 commit comments