2727import  com .phrase .client .model .TranslationKey ;
2828import  io .micrometer .core .instrument .MeterRegistry ;
2929import  io .micrometer .core .instrument .Timer ;
30+ import  java .time .Duration ;
31+ import  java .time .LocalDateTime ;
3032import  java .time .ZonedDateTime ;
3133import  java .time .format .DateTimeFormatter ;
3234import  java .util .AbstractMap .SimpleEntry ;
@@ -52,6 +54,7 @@ public class ThirdPartyTMSPhrase implements ThirdPartyTMS {
5254
5355  static  final  String  TAG_PREFIX  = "push_" ;
5456  static  final  String  TAG_PREFIX_WITH_REPOSITORY  = "push_%s" ;
57+   static  final  String  TAG_DATE_FORMAT  = "yyyy_MM_dd_HH_mm_ss_SSS" ;
5558  static  final  boolean  NATIVE_CLIENT_DEFAULT_VALUE  = false ;
5659
5760  static  Logger  logger  = LoggerFactory .getLogger (ThirdPartyTMSPhrase .class );
@@ -249,12 +252,22 @@ public void removeUnusedKeysAndTags(
249252            .filter (Objects ::nonNull )
250253            .filter (tagName  -> tagName .startsWith (TAG_PREFIX ))
251254            .filter (tagName  -> !allActiveTags .contains (tagName ))
255+             // That's to handle concurrent sync and make sure a tag that was just pushed is not 
256+             // deleted before the  pull from the same sync is finished. 
257+             // We don't prevent syncs to run concurrently 
258+             .filter (tagName  -> !areTagsWithin5Minutes (tagForUpload , tagName ))
252259            .toList ();
253260
254261    logger .info ("Tags to delete: {}" , pushTagsToDelete );
255262    phraseClient .deleteTags (projectId , pushTagsToDelete );
263+     logger .info ("RemoveUnusedKeysAndTags took: {}" , stopwatchRemoveUnusedKeysAndTags );
264+   }
256265
257-     logger .info ("removeUnusedKeysAndTags took: {}" , stopwatchRemoveUnusedKeysAndTags );
266+   static  boolean  areTagsWithin5Minutes (String  tagName1 , String  tagName2 ) {
267+     return  Duration .between (uploadTagToLocalDateTime (tagName2 ), uploadTagToLocalDateTime (tagName1 ))
268+             .abs ()
269+             .getSeconds ()
270+         < (60  * 5 );
258271  }
259272
260273  private  List <TextUnitDTO > getSourceTextUnitDTOs (
@@ -293,7 +306,7 @@ private List<TextUnitDTO> getSourceTextUnitDTOsPluralOnly(
293306
294307  public  static  String  getTagForUpload (String  repositoryName ) {
295308    ZonedDateTime  zonedDateTime  = JSR310Migration .dateTimeNowInUTC ();
296-     DateTimeFormatter  formatter  = DateTimeFormatter .ofPattern ("yyyy_MM_dd_HH_mm_ss_SSS" );
309+     DateTimeFormatter  formatter  = DateTimeFormatter .ofPattern (TAG_DATE_FORMAT );
297310    return  normalizeTagName (
298311        "%s%s_%s_%s" 
299312            .formatted (
@@ -303,6 +316,27 @@ public static String getTagForUpload(String repositoryName) {
303316                Math .abs (UUID .randomUUID ().getLeastSignificantBits () % 1000 )));
304317  }
305318
319+   public  static  LocalDateTime  uploadTagToLocalDateTime (String  tag ) {
320+ 
321+     if  (tag  == null  || !tag .contains ("_" )) {
322+       throw  new  IllegalArgumentException ("Invalid tag format: "  + tag );
323+     }
324+ 
325+     int  dateEndIndex  = tag .lastIndexOf ('_' ); // last part is a random number 
326+     int  dateStartIndex  = dateEndIndex ;
327+ 
328+     for  (int  i  = 0 ; i  < 7 ; i ++) {
329+       dateStartIndex  = tag .lastIndexOf ('_' , dateStartIndex  - 1 );
330+       if  (dateStartIndex  == -1 ) {
331+         throw  new  IllegalArgumentException ("Invalid tag format: "  + tag );
332+       }
333+     }
334+ 
335+     String  dateTimePart  = tag .substring (dateStartIndex  + 1 , dateEndIndex );
336+     DateTimeFormatter  formatter  = DateTimeFormatter .ofPattern (TAG_DATE_FORMAT );
337+     return  LocalDateTime .parse (dateTimePart , formatter );
338+   }
339+ 
306340  private  static  String  getTagNamePrefixForRepository (String  repositoryName ) {
307341    return  normalizeTagName (TAG_PREFIX_WITH_REPOSITORY .formatted (repositoryName ));
308342  }
0 commit comments