From 88f1a8b11a45b44bedb89fb80ff97004e45f4bf3 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Thu, 06 Aug 2026 08:00:53 +0000
Subject: [PATCH] Reduce per-bind allocations on the bind hot path (#672)
---
opendj-server-legacy/src/main/java/org/opends/server/core/PasswordPolicyState.java | 74 ++++++++++++++++++++++++++++++++-----
opendj-server-legacy/src/main/java/org/opends/server/core/BindOperationBasis.java | 13 +++++-
2 files changed, 74 insertions(+), 13 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 095c997..e467221 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 @@
{
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;
@@ -484,9 +493,7 @@
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());
+ 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;
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 cdad80d..443b1ff 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
@@ -1963,27 +1963,50 @@
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 +2047,37 @@
}
/**
+ * 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.
*
* @param passwordValue The value for which to make the determination.
--
Gitblit v1.10.0