Skip to content

Commit

Permalink
Fix #1747: Unnecessary FK constraint in callback event table (#1750)
Browse files Browse the repository at this point in the history
  • Loading branch information
jnpsk authored Oct 23, 2024
1 parent eae92e4 commit 0b48e42
Show file tree
Hide file tree
Showing 16 changed files with 119 additions and 59 deletions.
26 changes: 13 additions & 13 deletions docs/Database-Structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -383,17 +383,17 @@ Table stores Callback URL Events to monitor processing of the callbacks.

#### Columns

| Name | Type | Info | Note |
|-------------------------|-------------|-------------------------------------------|------------------------------------------------------------------------------------|
| id | bigint | primary key | Identifier of the Callback URL Event. |
| application_callback_id | varchar(37) | foreign key: pa\_application\_callback.id | Reference to configuration of the Callback URL Event. |
| callback_data | text | - | Data payload of the Callback URL Event. |
| status | varchar(32) | - | Current status of the Callback URL Event. |
| timestamp_created | timestamp | - | Timestamp of the Callback URL Event creation. |
| timestamp_last_call | timestamp | - | Timestamp of the last attempt to send the Callback URL Event. |
| timestamp_next_call | timestamp | - | Timestamp of the next scheduled time to send the Callback URL Event. |
| timestamp_delete_after | timestamp | - | Timestamp after which the Callback URL Event record can be deleted from the table. |
| timestamp_rerun_after | timestamp | - | Timestamp after which the Callback URL Event in processing state will be rerun. |
| attempts | integer | - | Number of dispatch attempts made for the Callback URL Event. |
| idempotency_key | varchar(36) | - | Idempotency key associated with the Callback URL Event. |
| Name | Type | Info | Note |
|-------------------------|-------------|-------------|------------------------------------------------------------------------------------------|
| id | bigint | primary key | Identifier of the Callback URL Event. |
| application_callback_id | varchar(37) | - | Reference to configuration of the Callback URL Event in `pa_application_callback` table. |
| callback_data | text | - | Data payload of the Callback URL Event. |
| status | varchar(32) | - | Current status of the Callback URL Event. |
| timestamp_created | timestamp | - | Timestamp of the Callback URL Event creation. |
| timestamp_last_call | timestamp | - | Timestamp of the last attempt to send the Callback URL Event. |
| timestamp_next_call | timestamp | - | Timestamp of the next scheduled time to send the Callback URL Event. |
| timestamp_delete_after | timestamp | - | Timestamp after which the Callback URL Event record can be deleted from the table. |
| timestamp_rerun_after | timestamp | - | Timestamp after which the Callback URL Event in processing state will be rerun. |
| attempts | integer | - | Number of dispatch attempts made for the Callback URL Event. |
| idempotency_key | varchar(36) | - | Idempotency key associated with the Callback URL Event. |
<!-- end -->
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<constraints primaryKey="true" />
</column>
<column name="application_callback_id" type="varchar(37)">
<constraints nullable="false" foreignKeyName="pa_application_callback_id_fk" referencedTableName="pa_application_callback" referencedColumnNames="id" />
<constraints nullable="false" />
</column>
<column name="callback_data" type="text">
<constraints nullable="false" />
Expand Down
Binary file modified docs/images/arch_db_structure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/sql/mssql/migration_1.8.0_1.9.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ GO

-- Changeset powerauth-java-server/1.9.x/20240704-callback-event-table.xml::1::Jan Pesek
-- Create a new table pa_callback_event
CREATE TABLE pa_application_callback_event (id bigint NOT NULL, application_callback_id varchar(37) NOT NULL, callback_data varchar (max) NOT NULL, status varchar(32) NOT NULL, timestamp_created datetime2(6) CONSTRAINT DF_pa_application_callback_event_timestamp_created DEFAULT GETDATE() NOT NULL, timestamp_last_call datetime2(6), timestamp_next_call datetime2(6), timestamp_delete_after datetime2(6), timestamp_rerun_after datetime2(6), attempts int CONSTRAINT DF_pa_application_callback_event_attempts DEFAULT 0 NOT NULL, idempotency_key varchar(36) NOT NULL, CONSTRAINT PK_PA_APPLICATION_CALLBACK_EVENT PRIMARY KEY (id), CONSTRAINT pa_application_callback_id_fk FOREIGN KEY (application_callback_id) REFERENCES pa_application_callback(id));
CREATE TABLE pa_application_callback_event (id bigint NOT NULL, application_callback_id varchar(37) NOT NULL, callback_data varchar (max) NOT NULL, status varchar(32) NOT NULL, timestamp_created datetime2(6) CONSTRAINT DF_pa_application_callback_event_timestamp_created DEFAULT GETDATE() NOT NULL, timestamp_last_call datetime2(6), timestamp_next_call datetime2(6), timestamp_delete_after datetime2(6), timestamp_rerun_after datetime2(6), attempts int CONSTRAINT DF_pa_application_callback_event_attempts DEFAULT 0 NOT NULL, idempotency_key varchar(36) NOT NULL, CONSTRAINT PK_PA_APPLICATION_CALLBACK_EVENT PRIMARY KEY (id));
GO

-- Changeset powerauth-java-server/1.9.x/20240704-callback-event-table.xml::2::Jan Pesek
Expand Down
2 changes: 1 addition & 1 deletion docs/sql/oracle/migration_1.8.0_1.9.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ ALTER TABLE pa_activation ADD commit_phase INTEGER DEFAULT '0';

-- Changeset powerauth-java-server/1.9.x/20240704-callback-event-table.xml::1::Jan Pesek
-- Create a new table pa_callback_event
CREATE TABLE pa_application_callback_event (id NUMBER(38, 0) NOT NULL, application_callback_id VARCHAR2(37) NOT NULL, callback_data CLOB NOT NULL, status VARCHAR2(32) NOT NULL, timestamp_created TIMESTAMP(6) DEFAULT sysdate NOT NULL, timestamp_last_call TIMESTAMP(6), timestamp_next_call TIMESTAMP(6), timestamp_delete_after TIMESTAMP(6), timestamp_rerun_after TIMESTAMP(6), attempts INTEGER DEFAULT 0 NOT NULL, idempotency_key VARCHAR2(36) NOT NULL, CONSTRAINT PK_PA_APPLICATION_CALLBACK_EVE PRIMARY KEY (id), CONSTRAINT pa_application_callback_id_fk FOREIGN KEY (application_callback_id) REFERENCES pa_application_callback(id));
CREATE TABLE pa_application_callback_event (id NUMBER(38, 0) NOT NULL, application_callback_id VARCHAR2(37) NOT NULL, callback_data CLOB NOT NULL, status VARCHAR2(32) NOT NULL, timestamp_created TIMESTAMP(6) DEFAULT sysdate NOT NULL, timestamp_last_call TIMESTAMP(6), timestamp_next_call TIMESTAMP(6), timestamp_delete_after TIMESTAMP(6), timestamp_rerun_after TIMESTAMP(6), attempts INTEGER DEFAULT 0 NOT NULL, idempotency_key VARCHAR2(36) NOT NULL, CONSTRAINT PK_PA_APPLICATION_CALLBACK_EVE PRIMARY KEY (id));

-- Changeset powerauth-java-server/1.9.x/20240704-callback-event-table.xml::2::Jan Pesek
-- Add max_attempts column to pa_application_callback table.
Expand Down
2 changes: 1 addition & 1 deletion docs/sql/postgresql/migration_1.8.0_1.9.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ ALTER TABLE pa_activation ADD commit_phase INTEGER DEFAULT 0;

-- Changeset powerauth-java-server/1.9.x/20240704-callback-event-table.xml::1::Jan Pesek
-- Create a new table pa_callback_event
CREATE TABLE pa_application_callback_event (id BIGINT NOT NULL, application_callback_id VARCHAR(37) NOT NULL, callback_data TEXT NOT NULL, status VARCHAR(32) NOT NULL, timestamp_created TIMESTAMP(6) WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, timestamp_last_call TIMESTAMP(6) WITHOUT TIME ZONE, timestamp_next_call TIMESTAMP(6) WITHOUT TIME ZONE, timestamp_delete_after TIMESTAMP(6) WITHOUT TIME ZONE, timestamp_rerun_after TIMESTAMP(6) WITHOUT TIME ZONE, attempts INTEGER DEFAULT 0 NOT NULL, idempotency_key VARCHAR(36) NOT NULL, CONSTRAINT pa_application_callback_event_pkey PRIMARY KEY (id), CONSTRAINT pa_application_callback_id_fk FOREIGN KEY (application_callback_id) REFERENCES pa_application_callback(id));
CREATE TABLE pa_application_callback_event (id BIGINT NOT NULL, application_callback_id VARCHAR(37) NOT NULL, callback_data TEXT NOT NULL, status VARCHAR(32) NOT NULL, timestamp_created TIMESTAMP(6) WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, timestamp_last_call TIMESTAMP(6) WITHOUT TIME ZONE, timestamp_next_call TIMESTAMP(6) WITHOUT TIME ZONE, timestamp_delete_after TIMESTAMP(6) WITHOUT TIME ZONE, timestamp_rerun_after TIMESTAMP(6) WITHOUT TIME ZONE, attempts INTEGER DEFAULT 0 NOT NULL, idempotency_key VARCHAR(36) NOT NULL, CONSTRAINT pa_application_callback_event_pkey PRIMARY KEY (id));

-- Changeset powerauth-java-server/1.9.x/20240704-callback-event-table.xml::2::Jan Pesek
-- Add max_attempts column to pa_application_callback table.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.springframework.data.util.ProxyUtils;

import java.io.Serial;
Expand Down Expand Up @@ -50,9 +52,8 @@ public class CallbackUrlEventEntity implements Serializable {
@Column(name = "id")
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "application_callback_id", referencedColumnName = "id", nullable = false, updatable = false)
private CallbackUrlEntity callbackUrlEntity;
@Column(name = "application_callback_id", updatable = false, nullable = false)
private String callbackUrlEntityId;

@Column(name = "callback_data", nullable = false)
@Convert(converter = MapToJsonConverter.class)
Expand Down Expand Up @@ -101,7 +102,7 @@ public int hashCode() {
public String toString() {
return "CallbackUrlEventEntity{" +
"id=" + id +
", callbackUrlEntity=" + (callbackUrlEntity == null ? "null" : callbackUrlEntity.getId()) +
", callbackUrlEntityId=" + callbackUrlEntityId +
", status=" + status +
", attempts=" + attempts +
'}';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ private void notifyCallbackUrl(CallbackUrlEntity callbackUrlEntity, Map<String,
}

final CallbackUrlEventEntity callbackUrlEventEntity = callbackUrlEventService.createAndSaveEventForProcessing(callbackUrlEntity, callbackData);
final CallbackUrlEvent callbackUrlEvent = CallbackUrlConvertor.convert(callbackUrlEventEntity, callbackUrlEntity.getId());
final CallbackUrlEvent callbackUrlEvent = CallbackUrlConvertor.convert(callbackUrlEventEntity, callbackUrlEntity);
TransactionUtils.executeAfterTransactionCommits(
() -> enqueue(callbackUrlEvent)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import com.github.benmanes.caffeine.cache.LoadingCache;
import io.getlime.security.powerauth.app.server.configuration.PowerAuthCallbacksConfiguration;
import io.getlime.security.powerauth.app.server.database.model.entity.CallbackUrlEntity;
import io.getlime.security.powerauth.app.server.database.model.entity.CallbackUrlEventEntity;
import io.getlime.security.powerauth.app.server.database.model.enumeration.CallbackUrlEventStatus;
import io.getlime.security.powerauth.app.server.database.repository.CallbackUrlEventRepository;
Expand Down Expand Up @@ -60,16 +59,16 @@ public void handleSuccess(final CallbackUrlEvent callbackUrlEvent) {
final CallbackUrlEventEntity callbackUrlEventEntity = callbackUrlEventRepository.findById(callbackUrlEvent.entityId())
.orElseThrow(() -> new IllegalStateException("Callback Url Event was not found in database during its success handling: callbackUrlEventId=" + callbackUrlEvent.entityId()));

logger.info("Callback succeeded, URL={}, callbackEventId={}", callbackUrlEventEntity.getCallbackUrlEntity().getCallbackUrl(), callbackUrlEventEntity.getId());
logger.info("Callback succeeded, URL={}, callbackEventId={}", callbackUrlEvent.config().url(), callbackUrlEventEntity.getId());

final Duration retentionPeriod = Objects.requireNonNullElse(callbackUrlEventEntity.getCallbackUrlEntity().getRetentionPeriod(), powerAuthCallbacksConfiguration.getDefaultRetentionPeriod());
final Duration retentionPeriod = Objects.requireNonNullElse(callbackUrlEvent.config().retentionPeriod(), powerAuthCallbacksConfiguration.getDefaultRetentionPeriod());
callbackUrlEventEntity.setTimestampDeleteAfter(LocalDateTime.now().plus(retentionPeriod));
callbackUrlEventEntity.setTimestampNextCall(null);
callbackUrlEventEntity.setTimestampRerunAfter(null);
callbackUrlEventEntity.setAttempts(callbackUrlEventEntity.getAttempts() + 1);
callbackUrlEventEntity.setStatus(CallbackUrlEventStatus.COMPLETED);
callbackUrlEventRepository.save(callbackUrlEventEntity);
resetFailureCount(callbackUrlEventEntity.getCallbackUrlEntity().getId());
resetFailureCount(callbackUrlEventEntity.getCallbackUrlEntityId());
}

/**
Expand All @@ -82,30 +81,29 @@ public void handleFailure(final CallbackUrlEvent callbackUrlEvent, final Throwab
final CallbackUrlEventEntity callbackUrlEventEntity = callbackUrlEventRepository.findById(callbackUrlEvent.entityId())
.orElseThrow(() -> new IllegalStateException("Callback Url Event was not found in database during its failure handling: callbackUrlEventId=" + callbackUrlEvent.entityId()));

logger.info("Callback failed, URL={}, callbackEventId={}, error={}", callbackUrlEventEntity.getCallbackUrlEntity().getCallbackUrl(), callbackUrlEventEntity.getId(), error.getMessage());
logger.info("Callback failed, URL={}, callbackEventId={}, error={}", callbackUrlEvent.config().url(), callbackUrlEventEntity.getId(), error.getMessage());

callbackUrlEventEntity.setAttempts(callbackUrlEventEntity.getAttempts() + 1);
callbackUrlEventEntity.setTimestampRerunAfter(null);

final CallbackUrlEntity callbackUrlEntity = callbackUrlEventEntity.getCallbackUrlEntity();
final int maxAttempts = Objects.requireNonNullElse(callbackUrlEntity.getMaxAttempts(), powerAuthCallbacksConfiguration.getDefaultMaxAttempts());
final int maxAttempts = Objects.requireNonNullElse(callbackUrlEvent.config().maxAttempts(), powerAuthCallbacksConfiguration.getDefaultMaxAttempts());
final int attemptsMade = callbackUrlEventEntity.getAttempts();

if (attemptsMade < maxAttempts) {
final Duration initialBackoff = Objects.requireNonNullElse(callbackUrlEntity.getInitialBackoff(), powerAuthCallbacksConfiguration.getDefaultInitialBackoff());
final Duration initialBackoff = Objects.requireNonNullElse(callbackUrlEvent.config().initialBackoff(), powerAuthCallbacksConfiguration.getDefaultInitialBackoff());
final Duration backoffPeriod = calculateExponentialBackoffPeriod(callbackUrlEventEntity.getAttempts(), initialBackoff, powerAuthCallbacksConfiguration.getBackoffMultiplier(), powerAuthCallbacksConfiguration.getMaxBackoff());
callbackUrlEventEntity.setTimestampNextCall(LocalDateTime.now().plus(backoffPeriod));
callbackUrlEventEntity.setStatus(CallbackUrlEventStatus.PENDING);
} else {
logger.debug("Maximum number of attempts reached for callbackUrlEventId={}", callbackUrlEventEntity.getId());
final Duration retentionPeriod = Objects.requireNonNullElse(callbackUrlEventEntity.getCallbackUrlEntity().getRetentionPeriod(), powerAuthCallbacksConfiguration.getDefaultRetentionPeriod());
final Duration retentionPeriod = Objects.requireNonNullElse(callbackUrlEvent.config().retentionPeriod(), powerAuthCallbacksConfiguration.getDefaultRetentionPeriod());
callbackUrlEventEntity.setTimestampDeleteAfter(LocalDateTime.now().plus(retentionPeriod));
callbackUrlEventEntity.setTimestampNextCall(null);
callbackUrlEventEntity.setStatus(CallbackUrlEventStatus.FAILED);
}

callbackUrlEventRepository.save(callbackUrlEventEntity);
incrementFailureCount(callbackUrlEntity.getId());
incrementFailureCount(callbackUrlEventEntity.getCallbackUrlEntityId());
}

/**
Expand Down Expand Up @@ -137,6 +135,7 @@ private void incrementFailureCount(final String callbackUrlId) {
.timestampCreated(cached.timestampCreated())
.failureCount(cached.failureCount() + 1)
.timestampLastFailure(LocalDateTime.now())
.callbackUrlEntity(cached.callbackUrlEntity())
.build()
);
}
Expand All @@ -152,6 +151,7 @@ private void resetFailureCount(final String callbackUrlId) {
.timestampCreated(cached.timestampCreated())
.failureCount(0)
.timestampLastFailure(cached.timestampLastFailure())
.callbackUrlEntity(cached.callbackUrlEntity())
.build()
);
}
Expand Down
Loading

0 comments on commit 0b48e42

Please sign in to comment.