From ff93f3bf42f4e2ed6e3cd90bb7983e4754e25bae Mon Sep 17 00:00:00 2001
From: Valera V Harseko <vharseko@3a-systems.ru>
Date: Mon, 22 Jun 2026 10:55:39 +0000
Subject: [PATCH] Refine LDAP benchmark: READD, SSHA-256 hashing, grouped charts

---
 .github/benchmark/summary.sh |   55 ++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 34 insertions(+), 21 deletions(-)

diff --git a/.github/benchmark/summary.sh b/.github/benchmark/summary.sh
index ecac86c..193fb33 100644
--- a/.github/benchmark/summary.sh
+++ b/.github/benchmark/summary.sh
@@ -32,7 +32,7 @@
 DJ_IMG="${6:-}"
 
 # Operations to compare, in workflow order. ADMIN_CONNECT and Total are excluded.
-OPS=("ADD" "SEARCH" "COMPARE" "MODIFY" "BIND" "DELETE" "ADD WITHOUT DELETE")
+OPS=("ADD" "SEARCH" "COMPARE" "MODIFY" "BIND" "DELETE" "READD")
 
 # m <file> <label> <field>  -> numeric value (0 if absent), rounded to 1 decimal.
 m() { jq -r --arg l "$2" --arg f "$3" '((.[$l][$f]) // 0) | (.*10 | round / 10)' "$1"; }
@@ -85,51 +85,63 @@
 echo ""
 
 # ---------------------------------------------------------------- Chart helpers
-# Build a Mermaid list "x, y, z" of values for all OPS from <file> <field>, via <m|mi>.
-series() { # <fn> <file> <field>
-  local fn="$1" file="$2" field="$3" out="" v
-  for op in "${OPS[@]}"; do
-    v=$("$fn" "$file" "$op" "$field")
-    out+="${out:+, }${v}"
-  done
+# Mermaid xychart-beta has no grouped bars/legend, so to get two adjacent (non-overlapping)
+# columns per operation we repeat each op on the x-axis and zero-pad the two series: OpenLDAP
+# bars land on the left tick of each pair, OpenDJ on the right tick.
+#
+# x-axis: each op twice -> "ADD", "ADD", "SEARCH", "SEARCH", ...
+xaxis_pairs() {
+  local out="" op
+  for op in "${OPS[@]}"; do out+="${out:+, }\"${op}\", \"${op}\""; done
   printf '%s' "$out"
 }
-# x-axis with every label quoted (labels contain spaces).
-xaxis() {
-  local out="" op
-  for op in "${OPS[@]}"; do out+="${out:+, }\"${op}\""; done
+# OpenLDAP series: value then 0 per op (bar on the left tick of each pair).
+series_ol() { # <fn> <file> <field>
+  local fn="$1" file="$2" field="$3" out="" v
+  for op in "${OPS[@]}"; do v=$("$fn" "$file" "$op" "$field"); out+="${out:+, }${v}, 0"; done
+  printf '%s' "$out"
+}
+# OpenDJ series: 0 then value per op (bar on the right tick of each pair).
+series_dj() { # <fn> <file> <field>
+  local fn="$1" file="$2" field="$3" out="" v
+  for op in "${OPS[@]}"; do v=$("$fn" "$file" "$op" "$field"); out+="${out:+, }0, ${v}"; done
   printf '%s' "$out"
 }
 
-XAXIS="$(xaxis)"
+XAXIS="$(xaxis_pairs)"
+# Fix the two series colors (series 1 = OpenLDAP blue, series 2 = OpenDJ orange).
+PALETTE='%%{init: {"themeVariables": {"xyChart": {"plotColorPalette": "#4e79a7, #f28e2b"}}}}%%'
+CAPTION="_Each operation has two columns: ๐ŸŸฆ OpenLDAP (left) ยท ๐ŸŸง OpenDJ (right)._"
 
 # ---------------------------------------------------------------- Throughput chart
 echo "### Comparative chart โ€” throughput per operation (ops/s, higher is better)"
 echo ""
-echo "_First bar series = OpenLDAP, second bar series = OpenDJ._"
+echo "$CAPTION"
 echo ""
 echo '```mermaid'
+echo "$PALETTE"
 echo "xychart-beta"
 echo "    title \"Throughput per operation (ops/s) โ€” OpenLDAP vs OpenDJ\""
 echo "    x-axis [${XAXIS}]"
 echo "    y-axis \"ops/s\""
-echo "    bar [$(series m "$OL_JSON" throughput)]"
-echo "    bar [$(series m "$DJ_JSON" throughput)]"
+echo "    bar [$(series_ol m "$OL_JSON" throughput)]"
+echo "    bar [$(series_dj m "$DJ_JSON" throughput)]"
 echo '```'
 echo ""
 
 # ---------------------------------------------------------------- Latency chart
 echo "### Comparative chart โ€” mean latency per operation (ms, lower is better)"
 echo ""
-echo "_First bar series = OpenLDAP, second bar series = OpenDJ._"
+echo "$CAPTION"
 echo ""
 echo '```mermaid'
+echo "$PALETTE"
 echo "xychart-beta"
 echo "    title \"Mean latency per operation (ms) โ€” OpenLDAP vs OpenDJ\""
 echo "    x-axis [${XAXIS}]"
 echo "    y-axis \"ms\""
-echo "    bar [$(series m "$OL_JSON" meanResTime)]"
-echo "    bar [$(series m "$DJ_JSON" meanResTime)]"
+echo "    bar [$(series_ol m "$OL_JSON" meanResTime)]"
+echo "    bar [$(series_dj m "$DJ_JSON" meanResTime)]"
 echo '```'
 echo ""
 
@@ -139,6 +151,7 @@
 echo "- \`BIND\` is the measured **user authentication** (\`test=sbind\`, single bind/unbind on its"
 echo "  own connection) as \`cn=user_<n>,ou=People\` with the password set by \`MODIFY\`. The admin"
 echo "  connection bind (\`ADMIN_CONNECT\`) is cached once per thread and excluded from these results."
-echo "- Default password storage scheme differs by server (OpenDJ PBKDF2/Salted-SHA vs OpenLDAP"
-echo "  SSHA); this is a legitimate part of authentication cost."
+echo "- MODIFY sends the password in cleartext; each server hashes it on write with the **same"
+echo "  scheme (SSHA-256)** โ€” OpenLDAP via the pw-sha2 module + ppolicy hash-cleartext, OpenDJ via"
+echo "  its Salted SHA-256 default scheme โ€” so BIND authentication is compared on equal footing."
 echo "- Full interactive JMeter HTML dashboards are attached as the \`jmeter-reports\` artifact."

--
Gitblit v1.10.0