From 89cdbf22e6412663adb6afb95b4033ff83551cff Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Thu, 2 Jul 2026 22:25:09 +0300 Subject: [PATCH 1/2] Reduce per-bind allocations on the bind hot path Two allocation sites ran unconditionally for every bind: - BindOperationBasis.run() built a CancelRequest and its localized message even when the bind was the only operation in progress (the common case on a persistent connection), so nothing could be cancelled. Build them only when other operations exist. - PasswordPolicyState.passwordMatches() materialized every stored password value as a String, split it into String[] components, and re-encoded the payload back into a ByteString before handing it to the storage scheme. Parse the {scheme} prefix at the byte level and pass the payload as a zero-copy sub-sequence instead; malformed values fall back to the String decoder to preserve error behaviour. The authPassword syntax path is unchanged. JFR TLAB profiling under the PR #660 benchmark scenario shows the password-matching share of allocations drop from 3.8% to 2.6% (String samples -40% at higher throughput); interleaved A/B rounds show +24..+29% throughput with lower p99.9 on an 8-core host. --- .../server/core/BindOperationBasis.java | 14 +++- .../server/core/PasswordPolicyState.java | 75 ++++++++++++++++--- 2 files changed, 75 insertions(+), 14 deletions(-) diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java b/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java index 957dddb213..4be9fe8069 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java @@ -13,6 +13,7 @@ * * Copyright 2007-2010 Sun Microsystems, Inc. * Portions Copyright 2013-2016 ForgeRock AS. + * Portions Copyright 2026 3A Systems, LLC */ package org.opends.server.core; @@ -482,10 +483,15 @@ public final void run() ClientConnection clientConnection = getClientConnection(); clientConnection.setUnauthenticated(); - // Abandon any operations that may be in progress for the client. - LocalizableMessage cancelReason = INFO_CANCELED_BY_BIND_REQUEST.get(); - CancelRequest cancelRequest = new CancelRequest(true, cancelReason); - clientConnection.cancelAllOperationsExcept(cancelRequest, getMessageID()); + // Abandon any operations that may be in progress for the client. Only + // build the cancel request when there is something to cancel: on a plain + // re-bind the bind itself is the only operation in progress. + if (clientConnection.getOperationsInProgress().size() > 1) + { + LocalizableMessage cancelReason = INFO_CANCELED_BY_BIND_REQUEST.get(); + CancelRequest cancelRequest = new CancelRequest(true, cancelReason); + clientConnection.cancelAllOperationsExcept(cancelRequest, getMessageID()); + } // This flag is set to true as soon as a workflow has been executed. boolean workflowExecuted = false; diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/PasswordPolicyState.java b/opendj-server-legacy/src/main/java/org/opends/server/core/PasswordPolicyState.java index 250bc6ff02..0463a38b37 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/core/PasswordPolicyState.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/core/PasswordPolicyState.java @@ -13,6 +13,7 @@ * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. + * Portions Copyright 2026 3A Systems, LLC */ package org.opends.server.core; @@ -1963,27 +1964,50 @@ public boolean passwordMatches(ByteString password) return false; } + boolean isAuthPassword = passwordPolicy.isAuthPasswordSyntax(); for (Attribute a : attrList) { for (ByteString v : a) { try { - String[] pwComponents = getPwComponents(v); - String schemeName = pwComponents[0]; - PasswordStorageScheme scheme = getPasswordStorageScheme(schemeName); - if (scheme == null) + String schemeName; + boolean matches; + if (isAuthPassword) { - if (logger.isTraceEnabled()) + String[] pwComponents = getPwComponents(v); + schemeName = pwComponents[0]; + PasswordStorageScheme scheme = getPasswordStorageScheme(schemeName); + if (scheme == null) { - logger.trace("User entry %s contains a password with scheme %s that is not defined in the server.", - userDNString, schemeName); + traceUndefinedScheme(schemeName); + continue; } - - continue; + matches = passwordMatches(password, pwComponents, scheme); + } + else + { + // This method runs for every bind: parse the {scheme} prefix at + // the byte level instead of materializing the whole stored value + // as a String only to convert its payload back to bytes. + int closePos = userPasswordSchemeEnd(v); + if (closePos < 0) + { + // Malformed value: raise the same errors as the String decoder. + getPwComponents(v); + continue; + } + schemeName = toLowerCase(v.subSequence(1, closePos).toString()); + PasswordStorageScheme scheme = DirectoryServer.getPasswordStorageScheme(schemeName); + if (scheme == null) + { + traceUndefinedScheme(schemeName); + continue; + } + matches = scheme.passwordMatches(password, v.subSequence(closePos + 1, v.length())); } - if (passwordMatches(password, pwComponents, scheme)) + if (matches) { if (logger.isTraceEnabled()) { @@ -2024,6 +2048,37 @@ private String[] getPwComponents(ByteString v) throws DirectoryException : UserPasswordSyntax.decodeUserPassword(v.toString()); } + /** + * Returns the index of the closing brace of the {scheme} prefix of the + * provided user password value, or -1 if the value is not well-formed + * (also when the scheme name is empty, mirroring + * {@link UserPasswordSyntax#decodeUserPassword(String)}). + */ + private static int userPasswordSchemeEnd(ByteString v) + { + if (v.length() == 0 || v.byteAt(0) != '{') + { + return -1; + } + for (int i = 1; i < v.length(); i++) + { + if (v.byteAt(i) == '}') + { + return i > 1 ? i : -1; + } + } + return -1; + } + + private void traceUndefinedScheme(String schemeName) + { + if (logger.isTraceEnabled()) + { + logger.trace("User entry %s contains a password with scheme %s that is not defined in the server.", + userDNString, schemeName); + } + } + /** * Indicates whether the provided password value is pre-encoded. * From 8938156bfb582c6bd105f24eccd25da2cb56a587 Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Sat, 4 Jul 2026 13:17:51 +0300 Subject: [PATCH 2/2] Share one immutable CancelRequest instead of pre-checking operations in progress MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The size() > 1 guard drew a review concern about a check-then-act window (new operations cannot in fact register while a bind is in progress — both LDAP stacks gate the connection — but the guard's benefit does not justify the debate). CancelRequest and LocalizableMessage are immutable, so a single shared instance restores the unconditional cancelAllOperationsExcept() call while dropping the per-bind allocations entirely — including on binds that do have operations to cancel. --- .../server/core/BindOperationBasis.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java b/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java index 4be9fe8069..22669d4026 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java @@ -53,6 +53,15 @@ public class BindOperationBasis { private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); + /** + * The cancel request sent to the other operations in progress when a bind + * starts. CancelRequest and LocalizableMessage are both immutable (the + * message is rendered per locale when used), so a single shared instance + * avoids building both objects on every bind. + */ + private static final CancelRequest CANCEL_ALL_BY_BIND_REQUEST = + new CancelRequest(true, INFO_CANCELED_BY_BIND_REQUEST.get()); + /** The credentials used for SASL authentication. */ private ByteString saslCredentials; @@ -483,15 +492,8 @@ public final void run() ClientConnection clientConnection = getClientConnection(); clientConnection.setUnauthenticated(); - // Abandon any operations that may be in progress for the client. Only - // build the cancel request when there is something to cancel: on a plain - // re-bind the bind itself is the only operation in progress. - if (clientConnection.getOperationsInProgress().size() > 1) - { - LocalizableMessage cancelReason = INFO_CANCELED_BY_BIND_REQUEST.get(); - CancelRequest cancelRequest = new CancelRequest(true, cancelReason); - clientConnection.cancelAllOperationsExcept(cancelRequest, getMessageID()); - } + // Abandon any operations that may be in progress for the client. + clientConnection.cancelAllOperationsExcept(CANCEL_ALL_BY_BIND_REQUEST, getMessageID()); // This flag is set to true as soon as a workflow has been executed. boolean workflowExecuted = false;