diff --git a/api/src/main/java/com/databasir/job/ProjectSyncJob.java b/api/src/main/java/com/databasir/job/ProjectSyncJob.java
index a7e2443..5d62ac8 100644
--- a/api/src/main/java/com/databasir/job/ProjectSyncJob.java
+++ b/api/src/main/java/com/databasir/job/ProjectSyncJob.java
@@ -1,5 +1,6 @@
 package com.databasir.job;
 
+import com.databasir.common.DatabasirException;
 import com.databasir.core.domain.project.service.ProjectService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -18,7 +19,11 @@ public class ProjectSyncJob implements Job {
     public void execute(JobExecutionContext context) throws JobExecutionException {
         JobDataMap dataMap = context.getMergedJobDataMap();
         Integer projectId = dataMap.getInt("projectId");
-        projectService.createSyncTask(projectId, -1, true);
+        try {
+            projectService.createSyncTask(projectId, -1, true);
+        } catch (DatabasirException e) {
+            log.warn("Failed to create sync task for project {}, {}", projectId, e.getMessage());
+        }
     }
 
 }
diff --git a/core/src/main/java/com/databasir/core/domain/DomainErrors.java b/core/src/main/java/com/databasir/core/domain/DomainErrors.java
index a4fa7c9..c488dab 100644
--- a/core/src/main/java/com/databasir/core/domain/DomainErrors.java
+++ b/core/src/main/java/com/databasir/core/domain/DomainErrors.java
@@ -45,7 +45,7 @@ public enum DomainErrors implements DatabasirErrors {
     INVALID_MOCK_DATA_SCRIPT("A_10029", "不合法的表达式"),
     CANNOT_DELETE_SELF("A_10030", "无法对自己执行删除账号操作"),
     DRIVER_CLASS_NOT_FOUND("A_10031", "获取驱动类名失败"),
-    ;
+    DATABASE_DOCUMENT_DUPLICATE_KEY("A_10032", "文档版本重复");
 
     private final String errCode;
 
diff --git a/core/src/main/java/com/databasir/core/domain/document/service/DocumentService.java b/core/src/main/java/com/databasir/core/domain/document/service/DocumentService.java
index 99c0e8b..d66218e 100644
--- a/core/src/main/java/com/databasir/core/domain/document/service/DocumentService.java
+++ b/core/src/main/java/com/databasir/core/domain/document/service/DocumentService.java
@@ -1,5 +1,6 @@
 package com.databasir.core.domain.document.service;
 
+import com.databasir.common.DatabasirException;
 import com.databasir.core.Databasir;
 import com.databasir.core.DatabasirConfig;
 import com.databasir.core.diff.Diffs;
@@ -21,6 +22,7 @@ import com.databasir.dao.value.DocumentDiscussionCountPojo;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.jooq.tools.StringUtils;
+import org.springframework.dao.DuplicateKeyException;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
@@ -146,7 +148,13 @@ public class DocumentService {
                                  Integer projectId) {
 
         var pojo = documentPojoConverter.toDatabasePojo(projectId, meta, version);
-        final Integer docId = databaseDocumentDao.insertAndReturnId(pojo);
+        final Integer docId;
+        try {
+            docId = databaseDocumentDao.insertAndReturnId(pojo);
+        } catch (DuplicateKeyException e) {
+            log.warn("ignore insert database document projectId={} version={}", projectId, version);
+            throw new DatabasirException(DomainErrors.DATABASE_DOCUMENT_DUPLICATE_KEY);
+        }
         meta.getTables().forEach(table -> {
             TableDocumentPojo tableMeta =
                     documentPojoConverter.toTablePojo(docId, table);
diff --git a/core/src/main/java/com/databasir/core/domain/project/service/ProjectService.java b/core/src/main/java/com/databasir/core/domain/project/service/ProjectService.java
index 661289a..def1458 100644
--- a/core/src/main/java/com/databasir/core/domain/project/service/ProjectService.java
+++ b/core/src/main/java/com/databasir/core/domain/project/service/ProjectService.java
@@ -188,12 +188,12 @@ public class ProjectService {
     @Transactional
     public Optional<Integer> createSyncTask(Integer projectId, Integer userId, boolean ignoreIfExists) {
         if (!projectDao.existsById(projectId)) {
-            log.info("create sync task failed, because project not exists, projectId={}", projectId);
-            return Optional.empty();
+            log.warn("create sync task failed, because project not exists, projectId={}", projectId);
+            throw DomainErrors.PROJECT_NOT_FOUND.exception();
         }
         var validTaskStatus = List.of(ProjectSyncTaskStatus.NEW, ProjectSyncTaskStatus.RUNNING);
         if (ignoreIfExists && projectSyncTaskDao.existsByProjectId(projectId, validTaskStatus)) {
-            log.info("create sync task failed, it's already exists, projectId={}", projectId);
+            log.warn("create sync task failed, it's already exists, projectId={}", projectId);
             return Optional.empty();
         }
         ProjectSyncTaskPojo projectSyncTask = new ProjectSyncTaskPojo();
diff --git a/dao/generated-src/jooq/main/java/com/databasir/dao/Indexes.java b/dao/generated-src/jooq/main/java/com/databasir/dao/Indexes.java
index a07d9f6..36802cd 100644
--- a/dao/generated-src/jooq/main/java/com/databasir/dao/Indexes.java
+++ b/dao/generated-src/jooq/main/java/com/databasir/dao/Indexes.java
@@ -5,7 +5,6 @@ package com.databasir.dao;
 
 
 import com.databasir.dao.tables.DataSourceProperty;
-import com.databasir.dao.tables.DatabaseDocument;
 import com.databasir.dao.tables.DocumentDiscussion;
 import com.databasir.dao.tables.ProjectSyncTask;
 import com.databasir.dao.tables.TableColumnDocument;
@@ -36,7 +35,6 @@ public class Indexes {
     public static final Index TABLE_FOREIGN_KEY_DOCUMENT_IDX_DATABASE_DOCUMENT_ID = Internal.createIndex(DSL.name("idx_database_document_id"), TableForeignKeyDocument.TABLE_FOREIGN_KEY_DOCUMENT, new OrderField[] { TableForeignKeyDocument.TABLE_FOREIGN_KEY_DOCUMENT.DATABASE_DOCUMENT_ID }, false);
     public static final Index TABLE_INDEX_DOCUMENT_IDX_DATABASE_DOCUMENT_ID = Internal.createIndex(DSL.name("idx_database_document_id"), TableIndexDocument.TABLE_INDEX_DOCUMENT, new OrderField[] { TableIndexDocument.TABLE_INDEX_DOCUMENT.DATABASE_DOCUMENT_ID }, false);
     public static final Index TABLE_TRIGGER_DOCUMENT_IDX_DATABASE_DOCUMENT_ID = Internal.createIndex(DSL.name("idx_database_document_id"), TableTriggerDocument.TABLE_TRIGGER_DOCUMENT, new OrderField[] { TableTriggerDocument.TABLE_TRIGGER_DOCUMENT.DATABASE_DOCUMENT_ID }, false);
-    public static final Index DATABASE_DOCUMENT_IDX_PROJECT_ID = Internal.createIndex(DSL.name("idx_project_id"), DatabaseDocument.DATABASE_DOCUMENT, new OrderField[] { DatabaseDocument.DATABASE_DOCUMENT.PROJECT_ID }, false);
     public static final Index DOCUMENT_DISCUSSION_IDX_PROJECT_ID = Internal.createIndex(DSL.name("idx_project_id"), DocumentDiscussion.DOCUMENT_DISCUSSION, new OrderField[] { DocumentDiscussion.DOCUMENT_DISCUSSION.PROJECT_ID }, false);
     public static final Index PROJECT_SYNC_TASK_IDX_PROJECT_ID = Internal.createIndex(DSL.name("idx_project_id"), ProjectSyncTask.PROJECT_SYNC_TASK, new OrderField[] { ProjectSyncTask.PROJECT_SYNC_TASK.PROJECT_ID }, false);
     public static final Index TABLE_COLUMN_DOCUMENT_IDX_TABLE_DOCUMENT_ID = Internal.createIndex(DSL.name("idx_table_document_id"), TableColumnDocument.TABLE_COLUMN_DOCUMENT, new OrderField[] { TableColumnDocument.TABLE_COLUMN_DOCUMENT.TABLE_DOCUMENT_ID }, false);
diff --git a/dao/generated-src/jooq/main/java/com/databasir/dao/Keys.java b/dao/generated-src/jooq/main/java/com/databasir/dao/Keys.java
index a59d5ce..3c580c1 100644
--- a/dao/generated-src/jooq/main/java/com/databasir/dao/Keys.java
+++ b/dao/generated-src/jooq/main/java/com/databasir/dao/Keys.java
@@ -76,6 +76,7 @@ public class Keys {
     public static final UniqueKey<DataSourceRecord> KEY_DATA_SOURCE_UK_PROJECT_ID = Internal.createUniqueKey(DataSource.DATA_SOURCE, DSL.name("KEY_data_source_uk_project_id"), new TableField[] { DataSource.DATA_SOURCE.PROJECT_ID }, true);
     public static final UniqueKey<DataSourcePropertyRecord> KEY_DATA_SOURCE_PROPERTY_PRIMARY = Internal.createUniqueKey(DataSourceProperty.DATA_SOURCE_PROPERTY, DSL.name("KEY_data_source_property_PRIMARY"), new TableField[] { DataSourceProperty.DATA_SOURCE_PROPERTY.ID }, true);
     public static final UniqueKey<DatabaseDocumentRecord> KEY_DATABASE_DOCUMENT_PRIMARY = Internal.createUniqueKey(DatabaseDocument.DATABASE_DOCUMENT, DSL.name("KEY_database_document_PRIMARY"), new TableField[] { DatabaseDocument.DATABASE_DOCUMENT.ID }, true);
+    public static final UniqueKey<DatabaseDocumentRecord> KEY_DATABASE_DOCUMENT_UK_PROJECT_ID_VERSION_IS_ARCHIVE = Internal.createUniqueKey(DatabaseDocument.DATABASE_DOCUMENT, DSL.name("KEY_database_document_uk_project_id_version_is_archive"), new TableField[] { DatabaseDocument.DATABASE_DOCUMENT.PROJECT_ID, DatabaseDocument.DATABASE_DOCUMENT.VERSION, DatabaseDocument.DATABASE_DOCUMENT.IS_ARCHIVE }, true);
     public static final UniqueKey<DatabaseTypeRecord> KEY_DATABASE_TYPE_PRIMARY = Internal.createUniqueKey(DatabaseType.DATABASE_TYPE, DSL.name("KEY_database_type_PRIMARY"), new TableField[] { DatabaseType.DATABASE_TYPE.ID }, true);
     public static final UniqueKey<DatabaseTypeRecord> KEY_DATABASE_TYPE_UK_DATABASE_TYPE_DELETED_DELETED_TOKEN = Internal.createUniqueKey(DatabaseType.DATABASE_TYPE, DSL.name("KEY_database_type_uk_database_type_deleted_deleted_token"), new TableField[] { DatabaseType.DATABASE_TYPE.DATABASE_TYPE_, DatabaseType.DATABASE_TYPE.DELETED, DatabaseType.DATABASE_TYPE.DELETED_TOKEN }, true);
     public static final UniqueKey<DocumentDescriptionRecord> KEY_DOCUMENT_DESCRIPTION_PRIMARY = Internal.createUniqueKey(DocumentDescription.DOCUMENT_DESCRIPTION, DSL.name("KEY_document_description_PRIMARY"), new TableField[] { DocumentDescription.DOCUMENT_DESCRIPTION.ID }, true);
diff --git a/dao/generated-src/jooq/main/java/com/databasir/dao/tables/DatabaseDocument.java b/dao/generated-src/jooq/main/java/com/databasir/dao/tables/DatabaseDocument.java
index 18ff589..8a4a7cb 100644
--- a/dao/generated-src/jooq/main/java/com/databasir/dao/tables/DatabaseDocument.java
+++ b/dao/generated-src/jooq/main/java/com/databasir/dao/tables/DatabaseDocument.java
@@ -5,7 +5,6 @@ package com.databasir.dao.tables;
 
 
 import com.databasir.dao.Databasir;
-import com.databasir.dao.Indexes;
 import com.databasir.dao.Keys;
 import com.databasir.dao.tables.records.DatabaseDocumentRecord;
 
@@ -16,7 +15,6 @@ import java.util.List;
 import org.jooq.Field;
 import org.jooq.ForeignKey;
 import org.jooq.Identity;
-import org.jooq.Index;
 import org.jooq.Name;
 import org.jooq.Record;
 import org.jooq.Row10;
@@ -141,11 +139,6 @@ public class DatabaseDocument extends TableImpl<DatabaseDocumentRecord> {
         return aliased() ? null : Databasir.DATABASIR;
     }
 
-    @Override
-    public List<Index> getIndexes() {
-        return Arrays.asList(Indexes.DATABASE_DOCUMENT_IDX_PROJECT_ID);
-    }
-
     @Override
     public Identity<DatabaseDocumentRecord, Integer> getIdentity() {
         return (Identity<DatabaseDocumentRecord, Integer>) super.getIdentity();
@@ -156,6 +149,11 @@ public class DatabaseDocument extends TableImpl<DatabaseDocumentRecord> {
         return Keys.KEY_DATABASE_DOCUMENT_PRIMARY;
     }
 
+    @Override
+    public List<UniqueKey<DatabaseDocumentRecord>> getUniqueKeys() {
+        return Arrays.asList(Keys.KEY_DATABASE_DOCUMENT_UK_PROJECT_ID_VERSION_IS_ARCHIVE);
+    }
+
     @Override
     public DatabaseDocument as(String alias) {
         return new DatabaseDocument(DSL.name(alias), this);
diff --git a/dao/src/main/resources/db/migration/V1.4__project_uk.sql b/dao/src/main/resources/db/migration/V1.4__project_uk.sql
new file mode 100644
index 0000000..932dadf
--- /dev/null
+++ b/dao/src/main/resources/db/migration/V1.4__project_uk.sql
@@ -0,0 +1,4 @@
+ALTER TABLE database_document
+    ADD CONSTRAINT UNIQUE uk_project_id_version_is_archive (project_id, version, is_archive);
+ALTER TABLE database_document
+    DROP INDEX idx_project_id;
\ No newline at end of file