Browse Source

fix: duplicate project version when concurrency (#108)

master
vran 3 years ago
committed by GitHub
parent
commit
ad4b1d9fe0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      api/src/main/java/com/databasir/job/ProjectSyncJob.java
  2. 2
      core/src/main/java/com/databasir/core/domain/DomainErrors.java
  3. 10
      core/src/main/java/com/databasir/core/domain/document/service/DocumentService.java
  4. 6
      core/src/main/java/com/databasir/core/domain/project/service/ProjectService.java
  5. 2
      dao/generated-src/jooq/main/java/com/databasir/dao/Indexes.java
  6. 1
      dao/generated-src/jooq/main/java/com/databasir/dao/Keys.java
  7. 12
      dao/generated-src/jooq/main/java/com/databasir/dao/tables/DatabaseDocument.java
  8. 4
      dao/src/main/resources/db/migration/V1.4__project_uk.sql

7
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());
}
}
}

2
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;

10
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);

6
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();

2
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);

1
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);

12
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);

4
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;
Loading…
Cancel
Save