diff --git a/core/src/main/java/google/registry/flows/FlowModule.java b/core/src/main/java/google/registry/flows/FlowModule.java index 6e14afe9e..0aa634655 100644 --- a/core/src/main/java/google/registry/flows/FlowModule.java +++ b/core/src/main/java/google/registry/flows/FlowModule.java @@ -147,6 +147,13 @@ public class FlowModule { .map(IsolationLevel::value); } + @Provides + @FlowScope + @LogSqlStatements + boolean provideShouldLogSqlStatements(Class extends Flow> flowClass) { + return SqlStatementLoggingFlow.class.isAssignableFrom(flowClass); + } + @Provides @FlowScope @Superuser @@ -370,4 +377,9 @@ public class FlowModule { @Qualifier @Documented public @interface Transactional {} + + /** Dagger qualifier for if we should log all SQL statements in a flow. */ + @Qualifier + @Documented + public @interface LogSqlStatements {} } diff --git a/core/src/main/java/google/registry/flows/FlowRunner.java b/core/src/main/java/google/registry/flows/FlowRunner.java index cdc36b497..41c97d29c 100644 --- a/core/src/main/java/google/registry/flows/FlowRunner.java +++ b/core/src/main/java/google/registry/flows/FlowRunner.java @@ -19,6 +19,7 @@ import static google.registry.xml.XmlTransformer.prettyPrint; import com.google.common.flogger.FluentLogger; import google.registry.flows.FlowModule.DryRun; import google.registry.flows.FlowModule.InputXml; +import google.registry.flows.FlowModule.LogSqlStatements; import google.registry.flows.FlowModule.RegistrarId; import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.Transactional; @@ -49,6 +50,7 @@ public class FlowRunner { @Inject @DryRun boolean isDryRun; @Inject @Superuser boolean isSuperuser; @Inject @Transactional boolean isTransactional; + @Inject @LogSqlStatements boolean logSqlStatements; @Inject SessionMetadata sessionMetadata; @Inject Trid trid; @Inject FlowReporter flowReporter; @@ -97,7 +99,8 @@ public class FlowRunner { } catch (EppException e) { throw new EppRuntimeException(e); } - }); + }, + logSqlStatements); } catch (DryRunException e) { return e.output; } catch (EppRuntimeException e) { diff --git a/core/src/main/java/google/registry/flows/SqlStatementLoggingFlow.java b/core/src/main/java/google/registry/flows/SqlStatementLoggingFlow.java new file mode 100644 index 000000000..d5c13fbd7 --- /dev/null +++ b/core/src/main/java/google/registry/flows/SqlStatementLoggingFlow.java @@ -0,0 +1,24 @@ +// Copyright 2025 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.flows; + +/** + * Interface for a {@link Flow} that logs its SQL statements when running transactionally. + * + *
We don't wish to log all SQL statements ever executed (that'll create too much log bloat) but
+ * for some flows and some occasions we may wish to know precisely what SQL statements are being
+ * run.
+ */
+public interface SqlStatementLoggingFlow extends TransactionalFlow {}
diff --git a/core/src/main/java/google/registry/flows/domain/DomainDeleteFlow.java b/core/src/main/java/google/registry/flows/domain/DomainDeleteFlow.java
index 4dfeabfe3..3c97cdc26 100644
--- a/core/src/main/java/google/registry/flows/domain/DomainDeleteFlow.java
+++ b/core/src/main/java/google/registry/flows/domain/DomainDeleteFlow.java
@@ -55,6 +55,7 @@ import google.registry.flows.FlowModule.Superuser;
import google.registry.flows.FlowModule.TargetId;
import google.registry.flows.MutatingFlow;
import google.registry.flows.SessionMetadata;
+import google.registry.flows.SqlStatementLoggingFlow;
import google.registry.flows.annotations.ReportingSpec;
import google.registry.flows.custom.DomainDeleteFlowCustomLogic;
import google.registry.flows.custom.DomainDeleteFlowCustomLogic.AfterValidationParameters;
@@ -117,7 +118,7 @@ import org.joda.time.Duration;
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
*/
@ReportingSpec(ActivityReportField.DOMAIN_DELETE)
-public final class DomainDeleteFlow implements MutatingFlow {
+public final class DomainDeleteFlow implements MutatingFlow, SqlStatementLoggingFlow {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
diff --git a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManager.java b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManager.java
index 7410dba5c..7a7d4d5df 100644
--- a/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManager.java
+++ b/core/src/main/java/google/registry/persistence/transaction/JpaTransactionManager.java
@@ -21,6 +21,7 @@ import jakarta.persistence.Query;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.metamodel.Metamodel;
+import java.util.concurrent.Callable;
/** Sub-interface of {@link TransactionManager} which defines JPA related methods. */
public interface JpaTransactionManager extends TransactionManager {
@@ -92,4 +93,15 @@ public interface JpaTransactionManager extends TransactionManager {
/** Return the {@link TransactionIsolationLevel} used in the current transaction. */
TransactionIsolationLevel getCurrentTransactionIsolationLevel();
+
+ /** Executes the work with the given isolation level, possibly logging all SQL statements used. */
+