From fa70bd272a6f3cb75cad6ae9e3e9d4a9f9fdf88e Mon Sep 17 00:00:00 2001 From: guyben Date: Tue, 5 Sep 2017 10:50:59 -0700 Subject: [PATCH] Add tool to check if other requests are still running This is needed for the Lock.java enhancement where a lock will be implicitly released if the request owning it dies. No matter which solution we want for refactoring the Lock itself, we will need this class. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=167600314 --- .../monitoring/whitebox/WhiteboxModule.java | 12 +-- .../google/registry/request/RequestLogId.java | 31 +++++++ .../registry/request/RequestModule.java | 14 ++++ .../registry/util/RequestStatusChecker.java | 34 ++++++++ .../util/RequestStatusCheckerImpl.java | 83 +++++++++++++++++++ 5 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 java/google/registry/request/RequestLogId.java create mode 100644 java/google/registry/util/RequestStatusChecker.java create mode 100644 java/google/registry/util/RequestStatusCheckerImpl.java diff --git a/java/google/registry/monitoring/whitebox/WhiteboxModule.java b/java/google/registry/monitoring/whitebox/WhiteboxModule.java index 32728be14..9655b7c7e 100644 --- a/java/google/registry/monitoring/whitebox/WhiteboxModule.java +++ b/java/google/registry/monitoring/whitebox/WhiteboxModule.java @@ -20,7 +20,6 @@ import static google.registry.request.RequestParameters.extractRequiredParameter import com.google.api.services.bigquery.model.TableFieldSchema; import com.google.appengine.api.taskqueue.Queue; -import com.google.apphosting.api.ApiProxy; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import dagger.Module; @@ -28,6 +27,7 @@ import dagger.Provides; import dagger.multibindings.IntoMap; import dagger.multibindings.StringKey; import google.registry.request.Parameter; +import google.registry.request.RequestLogId; import google.registry.util.Clock; import java.util.UUID; import javax.inject.Named; @@ -39,8 +39,6 @@ import javax.servlet.http.HttpServletRequest; @Module public class WhiteboxModule { - private static final String REQUEST_LOG_ID = "com.google.appengine.runtime.request_log_id"; - @Provides @IntoMap @StringKey(EppMetric.TABLE_ID) @@ -71,16 +69,10 @@ public class WhiteboxModule { }; } - @Provides - @Named("requestLogId") - static String provideRequestLogId() { - return ApiProxy.getCurrentEnvironment().getAttributes().get(REQUEST_LOG_ID).toString(); - } - /** Provides an EppMetric builder with the request ID and startTimestamp already initialized. */ @Provides static EppMetric.Builder provideEppMetricBuilder( - @Named("requestLogId") String requestLogId, Clock clock) { + @RequestLogId String requestLogId, Clock clock) { return EppMetric.builderForRequest(requestLogId, clock); } diff --git a/java/google/registry/request/RequestLogId.java b/java/google/registry/request/RequestLogId.java new file mode 100644 index 000000000..5076c64a2 --- /dev/null +++ b/java/google/registry/request/RequestLogId.java @@ -0,0 +1,31 @@ +// Copyright 2017 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.request; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import javax.inject.Qualifier; + +/** + * Dagger qualifier for the AppEngine request_log_id. + * + *

This is the unique log identifier of the current request. + */ +@Retention(RUNTIME) +@Qualifier +@Documented +public @interface RequestLogId {} diff --git a/java/google/registry/request/RequestModule.java b/java/google/registry/request/RequestModule.java index a21b41720..68b18a89c 100644 --- a/java/google/registry/request/RequestModule.java +++ b/java/google/registry/request/RequestModule.java @@ -29,6 +29,8 @@ import google.registry.request.HttpException.UnsupportedMediaTypeException; import google.registry.request.auth.AuthResult; import google.registry.request.lock.LockHandler; import google.registry.request.lock.LockHandlerPassthrough; +import google.registry.util.RequestStatusChecker; +import google.registry.util.RequestStatusCheckerImpl; import java.io.IOException; import java.util.Map; import javax.servlet.http.HttpServletRequest; @@ -129,6 +131,18 @@ public final class RequestModule { return lockHandler; } + @Provides + static RequestStatusChecker provideRequestStatusChecker( + RequestStatusCheckerImpl requestStatusChecker) { + return requestStatusChecker; + } + + @Provides + @RequestLogId + static String provideRequestLogId(RequestStatusChecker requestStatusChecker) { + return requestStatusChecker.getLogId(); + } + @Provides @JsonPayload @SuppressWarnings("unchecked") diff --git a/java/google/registry/util/RequestStatusChecker.java b/java/google/registry/util/RequestStatusChecker.java new file mode 100644 index 000000000..327aab0a0 --- /dev/null +++ b/java/google/registry/util/RequestStatusChecker.java @@ -0,0 +1,34 @@ +// Copyright 2017 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.util; + +import java.io.Serializable; + +/** Used to query whether requests are still running. */ +public interface RequestStatusChecker extends Serializable { + + /** + * Returns the unique log identifier of the current request. + * + *

Multiple calls must return the same value during the same Request. + */ + public String getLogId(); + + /** + * Returns true if the given request is currently running. + */ + public boolean isRunning(String requestLogId); +} + diff --git a/java/google/registry/util/RequestStatusCheckerImpl.java b/java/google/registry/util/RequestStatusCheckerImpl.java new file mode 100644 index 000000000..23349e64c --- /dev/null +++ b/java/google/registry/util/RequestStatusCheckerImpl.java @@ -0,0 +1,83 @@ +// Copyright 2017 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.util; + +import com.google.appengine.api.log.LogQuery; +import com.google.appengine.api.log.LogService; +import com.google.appengine.api.log.LogServiceFactory; +import com.google.appengine.api.log.RequestLogs; +import com.google.apphosting.api.ApiProxy; +import com.google.apphosting.api.ApiProxy.Environment; +import com.google.common.collect.Iterables; +import java.util.Collections; +import javax.inject.Inject; + +/** Implementation of the {@link RequestStatusChecker} interface. */ +public class RequestStatusCheckerImpl implements RequestStatusChecker { + + private static final long serialVersionUID = -8161977032130865437L; + + private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); + + /** + * The key to {@link Environment#getAttributes}'s request_log_id value. + */ + private static final String REQUEST_LOG_ID_KEY = "com.google.appengine.runtime.request_log_id"; + + private static final LogService LOG_SERVICE = LogServiceFactory.getLogService(); + + @Inject public RequestStatusCheckerImpl() {} + + /** + * Returns the unique log identifier of the current request. + * + *

May be safely called multiple times, will always return the same result (within the same + * request). + * + * @see appengine documentation + */ + @Override + public String getLogId() { + String requestLogId = + ApiProxy.getCurrentEnvironment().getAttributes().get(REQUEST_LOG_ID_KEY).toString(); + logger.infofmt("Current requestLogId: %s", requestLogId); + return requestLogId; + } + + /** + * Returns true if the given request is currently running. + * + * @see appengine documentation + */ + @Override + public boolean isRunning(String requestLogId) { + RequestLogs requestLogs = + Iterables.getOnlyElement( + LOG_SERVICE.fetch( + LogQuery.Builder.withRequestIds( + Collections.singletonList(requestLogId))), + null); + // requestLogs will be null if that requestLogId isn't found at all, which also implies it's not + // running. + if (requestLogs == null) { + logger.infofmt( + "Queried an unrecognized requestLogId %s - assume it isn't running", requestLogId); + return false; + } + logger.infofmt( + "Found logs for requestLogId %s - isFinished: %s", requestLogId, requestLogs.isFinished()); + return !requestLogs.isFinished(); + } +}