Skip to content
This repository has been archived by the owner on Nov 14, 2024. It is now read-only.

Add new getLockState() debugging endpoint #5302

Merged
merged 6 commits into from
Mar 12, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
import com.palantir.lock.HeldLocksGrant;
import com.palantir.lock.HeldLocksToken;
import com.palantir.lock.LockClient;
import com.palantir.lock.LockDescriptor;
import com.palantir.lock.LockRefreshToken;
import com.palantir.lock.LockRequest;
import com.palantir.lock.LockResponse;
import com.palantir.lock.LockRpcClient;
import com.palantir.lock.LockServerOptions;
import com.palantir.lock.LockState;
import com.palantir.lock.SimpleHeldLocksToken;
import java.math.BigInteger;
import java.util.Optional;
Expand Down Expand Up @@ -155,4 +157,9 @@ public void logCurrentState(String namespace) {
// dump its logs out.
shortTimeoutProxy.logCurrentState(namespace);
}

@Override
public LockState getLockState(LockDescriptor lock) {
return shortTimeoutProxy.getLockState(lock);
}
}
5 changes: 5 additions & 0 deletions changelog/@unreleased/pr-5302.v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type: improvement
improvement:
description: Add new getLockState(LockDescriptor) debugging endpoint to the RemoteLockService, which allows for easier, more targeted debugging than would be possible with previous logCurrentState() endpoint. Provide a LockDescriptor, and get the current set of clients holding and waiting for the lock.
links:
- https://github.com/palantir/atlasdb/pull/5302
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ public void logCurrentState() {
delegate().logCurrentState();
}

@Override
public LockState getLockState(LockDescriptor lock) {
return delegate().getLockState(lock);
}

@Override
public void close() throws IOException {
if (delegate() instanceof Closeable) {
Expand Down
4 changes: 4 additions & 0 deletions lock-api/src/main/java/com/palantir/lock/LockRpcClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,8 @@ Set<LockRefreshToken> refreshLockRefreshTokens(
@POST
@Path("log-current-state")
void logCurrentState(@Safe @PathParam("namespace") String namespace);

@POST
@Path("get-debugging-lock-state")
LockState getLockState(LockDescriptor lock);
}
72 changes: 72 additions & 0 deletions lock-api/src/main/java/com/palantir/lock/LockState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* (c) Copyright 2021 Palantir Technologies Inc. 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 com.palantir.lock;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.util.List;
import java.util.Optional;
import org.immutables.value.Value;

@Value.Immutable
@JsonSerialize(as = ImmutableLockState.class)
@JsonDeserialize(as = ImmutableLockState.class)
public interface LockState {
boolean isWriteLocked();

boolean isFrozen();

List<LockClient> exactCurrentLockHolders();

List<LockHolder> holders();

List<LockRequester> requesters();

@Value.Immutable
@JsonSerialize(as = ImmutableLockHolder.class)
@JsonDeserialize(as = ImmutableLockHolder.class)
interface LockHolder {
LockClient client();

long creationDateMs();

long expirationDateMs();

int numOtherLocksHeld();

Optional<Long> versionId();

String requestingThread();
}

@Value.Immutable
@JsonSerialize(as = ImmutableLockRequester.class)
@JsonDeserialize(as = ImmutableLockRequester.class)
interface LockRequester {
LockClient client();

LockGroupBehavior lockGroupBehavior();

BlockingMode blockingMode();

Optional<TimeDuration> blockingDuration();

Optional<Long> versionId();

String requestingThread();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,8 @@ Optional<HeldLocksToken> lockAndGetHeldLocks(@Safe @PathParam("client") String c
@POST
@Path("log-current-state")
void logCurrentState();

@POST
@Path("get-debugging-lock-state")
LockState getLockState(LockDescriptor lock);
}
16 changes: 16 additions & 0 deletions lock-api/src/main/java/com/palantir/lock/RemoteLockService.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,20 @@ HeldLocksToken lockAndGetHeldLocks(@Safe @PathParam("client") String client, Loc
@POST
@Path("log-current-state")
void logCurrentState();

/**
* Returns the current locking and request state of the specified lock.
*
* Note that this is a best-effort endpoint, and not all parts of the the returned data
* structure is guaranteed to reflect the state of the server at a given point in time.
*
* Also note that, as this is a debugging endpoint, we do not enforce backcompat guarantees
* on this endpoint.
*/
@POST
@Idempotent
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("get-debugging-lock-state")
LockState getLockState(LockDescriptor lock);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@
import com.palantir.lock.HeldLocksGrant;
import com.palantir.lock.HeldLocksToken;
import com.palantir.lock.LockClient;
import com.palantir.lock.LockDescriptor;
import com.palantir.lock.LockRefreshToken;
import com.palantir.lock.LockRequest;
import com.palantir.lock.LockResponse;
import com.palantir.lock.LockRpcClient;
import com.palantir.lock.LockServerOptions;
import com.palantir.lock.LockState;
import com.palantir.lock.SimpleHeldLocksToken;
import com.palantir.tokens.auth.AuthHeader;
import java.math.BigInteger;
Expand Down Expand Up @@ -159,4 +161,9 @@ public long currentTimeMillis(String namespace) {
public void logCurrentState(String namespace) {
dialogueShimDelegate.logCurrentState(namespace);
}

@Override
public LockState getLockState(LockDescriptor lock) {
return dialogueShimDelegate.getLockState(lock);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
import com.palantir.lock.HeldLocksGrant;
import com.palantir.lock.HeldLocksToken;
import com.palantir.lock.LockClient;
import com.palantir.lock.LockDescriptor;
import com.palantir.lock.LockRefreshToken;
import com.palantir.lock.LockRequest;
import com.palantir.lock.LockResponse;
import com.palantir.lock.LockRpcClient;
import com.palantir.lock.LockServerOptions;
import com.palantir.lock.LockState;
import com.palantir.lock.NamespaceAgnosticLockRpcClient;
import com.palantir.lock.SimpleHeldLocksToken;
import java.math.BigInteger;
Expand Down Expand Up @@ -145,4 +147,9 @@ public long currentTimeMillis() {
public void logCurrentState() {
lockRpcClient.logCurrentState(namespace);
}

@Override
public LockState getLockState(LockDescriptor lock) {
return lockRpcClient.getLockState(lock);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
import com.palantir.lock.HeldLocksGrant;
import com.palantir.lock.HeldLocksToken;
import com.palantir.lock.LockClient;
import com.palantir.lock.LockDescriptor;
import com.palantir.lock.LockRefreshToken;
import com.palantir.lock.LockRequest;
import com.palantir.lock.LockResponse;
import com.palantir.lock.LockRpcClient;
import com.palantir.lock.LockServerOptions;
import com.palantir.lock.LockService;
import com.palantir.lock.LockState;
import com.palantir.lock.NamespaceAgnosticLockRpcClient;
import com.palantir.lock.SimpleHeldLocksToken;
import java.math.BigInteger;
Expand Down Expand Up @@ -157,4 +159,9 @@ public long currentTimeMillis() {
public void logCurrentState() {
namespaceAgnosticLockRpcClient.logCurrentState();
}

@Override
public LockState getLockState(LockDescriptor lock) {
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

surely namespaceAgnosticLockRpcClient.getLockState(lock);?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops!

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this right? If so, can we have a comment to explain?

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public LockServerLock(LockDescriptor descriptor, LockClientIndices clients) {
this.sync = new LockServerSync(clients);
}

/* package */ LockServerSync getSync() {
return sync;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we remove the package comment for uniformity. Also can we move the package-private method below public methods?

@Override
public LockDescriptor getDescriptor() {
return descriptor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ private synchronized void decrementReadCount(int clientIndex) {
}
}

private synchronized Iterable<Integer> getReadClients() {
/* package */ synchronized Iterable<Integer> getReadClients() {
if (readLockHolders == null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we don't use the "package" comment anywhere else in this class

return ImmutableList.of();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
import com.palantir.lock.HeldLocksGrant;
import com.palantir.lock.HeldLocksToken;
import com.palantir.lock.HeldLocksTokens;
import com.palantir.lock.ImmutableLockHolder;
import com.palantir.lock.ImmutableLockRequester;
import com.palantir.lock.ImmutableLockState;
import com.palantir.lock.LockClient;
import com.palantir.lock.LockCollection;
import com.palantir.lock.LockCollections;
Expand All @@ -62,6 +65,7 @@
import com.palantir.lock.LockServerConfigs;
import com.palantir.lock.LockServerOptions;
import com.palantir.lock.LockService;
import com.palantir.lock.LockState;
import com.palantir.lock.RemoteLockService;
import com.palantir.lock.SimpleHeldLocksToken;
import com.palantir.lock.SimpleTimeDuration;
Expand All @@ -85,6 +89,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -1128,6 +1133,73 @@ public LockServerOptions getLockServerOptions() {
return options;
}

@Override
public LockState getLockState(LockDescriptor descriptor) {
LockServerLock readWriteLock = (LockServerLock) descriptorToLockMap.getIfPresent(descriptor);
if (readWriteLock == null) {
return ImmutableLockState.builder()
.isWriteLocked(false)
.isFrozen(false)
.build();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declare a constant?


LockServerSync sync = readWriteLock.getSync();
List<LockClient> readHolders;
LockClient writeHolders;
boolean isFrozen;
boolean writeMode;
synchronized (sync) {
readHolders = ImmutableList.copyOf(Iterables.transform(sync.getReadClients(), clientIndices::fromIndex));
writeHolders = sync.getLockHolder();
isFrozen = sync.isFrozen();
writeMode = readHolders.isEmpty();
}
List<LockClient> lockHolders;
if (readHolders.isEmpty()) {
if (writeHolders == null) {
lockHolders = ImmutableList.of();
} else {
lockHolders = ImmutableList.of(writeHolders);
}
} else {
lockHolders = readHolders;
}
Copy link
Contributor

@sudiksha27 sudiksha27 Mar 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract out method, maybe something like -

Suggested change
List<LockClient> lockHolders;
if (readHolders.isEmpty()) {
if (writeHolders == null) {
lockHolders = ImmutableList.of();
} else {
lockHolders = ImmutableList.of(writeHolders);
}
} else {
lockHolders = readHolders;
}
private static List<LockClient> getLockClients(List<LockClient> readHolders, LockClient writeHolders) {
return readHolders.isEmpty()
? Optional.ofNullable(writeHolders).map(ImmutableList::of).orElseGet(ImmutableList::of)
: readHolders;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a lot of fanciness for not a lot of benefit, but either way's fine by me!


List<HeldLocksToken> heldLocks = new ArrayList<>();
heldLocksTokenMap.keySet().stream()
.filter(token -> token.getLockDescriptors().contains(descriptor))
.forEach(heldLocks::add);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
List<HeldLocksToken> heldLocks = new ArrayList<>();
heldLocksTokenMap.keySet().stream()
.filter(token -> token.getLockDescriptors().contains(descriptor))
.forEach(heldLocks::add);
List<HeldLocksToken> heldLocks = heldLocksTokenMap.keySet().stream()
.filter(token -> token.getLockDescriptors().contains(descriptor))
.collect(Collectors.toList());


Map<LockRequest, LockClient> requests = new HashMap<>();
outstandingLockRequestMultimap.forEach((client, request) -> {
if (request.getLockDescriptors().contains(descriptor)) {
requests.put(request, client);
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Map<LockRequest, LockClient> requests = new HashMap<>();
outstandingLockRequestMultimap.forEach((client, request) -> {
if (request.getLockDescriptors().contains(descriptor)) {
requests.put(request, client);
}
});
Map<LockRequest, LockClient> requests = KeyedStream.stream(outstandingLockRequestMultimap)
.filterEntries((client, request) -> request.getLockDescriptors().contains(descriptor))
.mapEntries((client, request) -> Maps.immutableEntry(request, client))
.collectToMap();


ImmutableLockState.Builder lockState = ImmutableLockState.builder()
.isWriteLocked(writeMode)
.exactCurrentLockHolders(lockHolders)
.isFrozen(isFrozen);
heldLocks.forEach(lock -> lockState.addHolders(ImmutableLockHolder.builder()
.client(lock.getClient())
.creationDateMs(lock.getCreationDateMs())
.expirationDateMs(lock.getExpirationDateMs())
.numOtherLocksHeld(lock.getLocks().size() - 1)
.versionId(Optional.ofNullable(lock.getVersionId()))
.requestingThread(lock.getRequestingThread())
.build()));
requests.forEach((request, client) -> lockState.addRequesters(ImmutableLockRequester.builder()
.client(client)
.lockGroupBehavior(request.getLockGroupBehavior())
.blockingMode(request.getBlockingMode())
.blockingDuration(Optional.ofNullable(request.getBlockingDuration()))
.versionId(Optional.ofNullable(request.getVersionId()))
.requestingThread(request.getCreatingThreadName())
.build()));
return lockState.build();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can extract util methods for above; these static methods could live in LockState

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think factories for creating a LockHolder/LockRequester from the request and/or lock object would be nice, though requests.forEach(...) in the body here seems fine to me

}

/**
* Prints the current state of the lock server to the logs. Useful for
* debugging.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
import com.palantir.lock.HeldLocksGrant;
import com.palantir.lock.HeldLocksToken;
import com.palantir.lock.LockClient;
import com.palantir.lock.LockDescriptor;
import com.palantir.lock.LockRefreshToken;
import com.palantir.lock.LockRequest;
import com.palantir.lock.LockResponse;
import com.palantir.lock.LockServerOptions;
import com.palantir.lock.LockService;
import com.palantir.lock.LockState;
import com.palantir.lock.SimpleHeldLocksToken;
import java.io.IOException;
import java.math.BigInteger;
Expand Down Expand Up @@ -150,6 +152,11 @@ public void logCurrentState() {
delegate.logCurrentState();
}

@Override
public LockState getLockState(LockDescriptor lock) {
return delegate.getLockState(lock);
}

@Override
public void close() throws IOException {
delegate.close();
Expand Down
Loading