[ 
https://issues.apache.org/jira/browse/KUDU-3779?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18088893#comment-18088893
 ] 

Daojiang Yan commented on KUDU-3779:
------------------------------------

h2. Benchmark plan and results for in-memory metadata replay on encrypted 
clusters

Attached: [^KUDU-3779-bench-logs.tar.gz]

h3. Goal

Verify that the in-memory metadata replay fast path introduced by KUDU-3778
delivers the same kind of speedup on *encrypted* clusters, and that
extending it does not regress the existing non-encrypted path.

h3. Environment

|| Item || Value ||
| Build | release  |
| Slow tests | {{KUDU_ALLOW_SLOW_TESTS=1}} |
| Test binary | {{log_block_manager-test}} |
| Filter | {{--gtest_filter='*InMemoryReplayStartupBenchmark*'}} |
| Reopens per pass | 5 ({{--startup_benchmark_reopen_times=5}}) |
| Container metadata format | native (LBM) |
| Comparison axis | {{--log_container_metadata_inmem_replay_threshold_bytes}}: 
{{0}} (streaming) vs {{64MiB}} (in-memory) |

h4. Host / runtime

Runs inside a Docker container; the container shares the host kernel
and writes test data under {{/tmp}}, which is backed by the container's
overlay filesystem.

|| Item || Value ||
| CPU | Intel(R) Xeon(R) Silver 4216 @ 2.10 GHz, 1 socket / 16 cores / 32 
threads |
| Memory | 62 GiB total, ~29 GiB available as buff/cache during the run |
| Host kernel | Linux 4.18.0-553.47.1.el8_10.x86_64 |
| Container userspace | CentOS Linux 7 (Core) |
| Filesystem at /tmp | overlay (Docker), ~3.7 TiB capacity |
| Underlying disk | sdb, 3.7 TiB, rotational HDD (rota=1); host also has a 
223.6 GiB SSD (sda) but it is not where /tmp lives |
| Containerized | Docker |


h3. Test groups

Four groups, mirroring the layout of the KUDU-3778 commit description.

h4. Group A: workload-scale sweep (encryption enabled)

Holds deletion at 90%, varies the total number of records and live blocks.

|| Test || batches x blocks/batch || dirs || live blocks || log file ||
| A_small  | 200 x 500    | 4 |  10k | A_small_enc.log  |
| A_medium | 500 x 1000   | 8 |  50k | A_medium_enc.log |
| A_large  | 1000 x 1000  | 8 | 100k | A_large_enc.log  |

h4. Group B: deletion-ratio sweep (encryption enabled)

Holds workload at 500 x 1000 / 8 dirs, varies
{{--startup_benchmark_deleted_block_percentage}}.

|| Test || deletion % || live blocks || log file ||
| B_del0  |  0% | 500k | B_del0_enc.log  |
| B_del50 | 50% | 250k | B_del50_enc.log |
| B_del90 | 90% |  50k | B_del90_enc.log |
| B_del99 | 99% |   5k | B_del99_enc.log |

h4. Group C: container-density sweep (encryption enabled)

Holds workload at 500 x 1000 / 8 dirs / 90% deletion, varies
{{--log_container_max_blocks}}. The first reopen of each pass is
excluded from the steady-state mean because it is dominated by
cold page cache.

|| Test || log_container_max_blocks || containers || log file ||
| C_maxb-1     | -1     |   3 | C_maxb-1_enc.log     |
| C_maxb10000  | 10000  |  51 | C_maxb10000_enc.log  |
| C_maxb1000   | 1000   | 367 | C_maxb1000_enc.log   |

h4. Group D: non-encryption regression check

Same workload as A_medium / C_maxb-1 (500 x 1000, 8 dirs, 90% deletion)
but encryption *disabled*. Compared against KUDU-3778 numbers for the
same configuration to confirm no regression.

|| Test || encryption || log file ||
| D_regression | disabled | D_regression_unenc.log |

h3. Reproduce

{code}
Group A:
# A-small: 200×500, 4 dirs (~10k live)
KUDU_ALLOW_SLOW_TESTS=1 build/latest/bin/log_block_manager-test \
  --gtest_filter='*InMemoryReplayStartupBenchmark/1' \
  --startup_benchmark_reopen_times=5 \
  --startup_benchmark_batch_count_for_testing=200 \
  --startup_benchmark_block_count_per_batch_for_testing=500 \
  --startup_benchmark_data_dir_count_for_testing=4 \
  --startup_benchmark_deleted_block_percentage=90 \
  --logtostderr 2>&1 | tee log/bench-3779/A_small_enc.log

# A-medium: 500×1000, 8 dirs (~50k live)
KUDU_ALLOW_SLOW_TESTS=1 build/latest/bin/log_block_manager-test \
  --gtest_filter='*InMemoryReplayStartupBenchmark/1' \
  --startup_benchmark_reopen_times=5 \
  --startup_benchmark_batch_count_for_testing=500 \
  --startup_benchmark_block_count_per_batch_for_testing=1000 \
  --startup_benchmark_data_dir_count_for_testing=8 \
  --startup_benchmark_deleted_block_percentage=90 \
  --logtostderr 2>&1 | tee log/bench-3779/A_medium_enc.log

# A-large: 1000×1000, 8 dirs (~101k live)
KUDU_ALLOW_SLOW_TESTS=1 build/latest/bin/log_block_manager-test \
  --gtest_filter='*InMemoryReplayStartupBenchmark/1' \
  --startup_benchmark_reopen_times=5 \
  --startup_benchmark_batch_count_for_testing=1000 \
  --startup_benchmark_block_count_per_batch_for_testing=1000 \
  --startup_benchmark_data_dir_count_for_testing=8 \
  --startup_benchmark_deleted_block_percentage=90 \
  --logtostderr 2>&1 | tee log/bench-3779/A_large_enc.log

Group B:
for p in 0 50 90 99; do
  KUDU_ALLOW_SLOW_TESTS=1 build/latest/bin/log_block_manager-test \
    --gtest_filter='*InMemoryReplayStartupBenchmark/1' \
    --startup_benchmark_reopen_times=5 \
    --startup_benchmark_batch_count_for_testing=500 \
    --startup_benchmark_block_count_per_batch_for_testing=1000 \
    --startup_benchmark_data_dir_count_for_testing=8 \
    --startup_benchmark_deleted_block_percentage=$p \
    --logtostderr 2>&1 | tee log/bench-3779/B_del${p}_enc.log
done

Group C:
for m in -1 10000 1000; do
  KUDU_ALLOW_SLOW_TESTS=1 build/latest/bin/log_block_manager-test \
    --gtest_filter='*InMemoryReplayStartupBenchmark/1' \
    --startup_benchmark_reopen_times=5 \
    --startup_benchmark_batch_count_for_testing=500 \
    --startup_benchmark_block_count_per_batch_for_testing=1000 \
    --startup_benchmark_data_dir_count_for_testing=8 \
    --startup_benchmark_deleted_block_percentage=90 \
    --log_container_max_blocks=$m \
    --logtostderr 2>&1 | tee log/bench-3779/C_maxb${m}_enc.log
done

Group D:
KUDU_ALLOW_SLOW_TESTS=1 build/latest/bin/log_block_manager-test \
  --gtest_filter='*InMemoryReplayStartupBenchmark/0' \
  --startup_benchmark_reopen_times=5 \
  --startup_benchmark_batch_count_for_testing=500 \
  --startup_benchmark_block_count_per_batch_for_testing=1000 \
  --startup_benchmark_data_dir_count_for_testing=8 \
  --startup_benchmark_deleted_block_percentage=90 \
  --logtostderr 2>&1 | tee log/bench-3779/D_regression_unenc.log



{code}

{{*/1}} selects the {{EncryptionEnabled}} parametrization; use {{*/0}}
for the non-encryption baseline (Group D).

h3. Results (5-reopen mean, ms; container-density group is steady-state)

|| Group || Test || streaming || in-memory || speedup ||
| A | A_small  | 202 |  115 | 1.76x |
| A | A_medium | 1016 |  604 | 1.68x |
| A | A_large  | 2091 | 1218 | 1.72x |
| B | B_del0   | 703 | 468 | 1.50x |
| B | B_del50  | 899 | 549 | 1.64x |
| B | B_del90  | 1034 | 578 | 1.79x |
| B | B_del99  | 1042 | 577 | 1.81x |
| C | C_maxb-1     | 1018 | 577 | 1.76x |
| C | C_maxb10000  |   54 |  34 | 1.59x |
| C | C_maxb1000   |   28 |  22 | 1.27x |
| D | D_regression (non-encrypted) | 805 | 381 | 2.11x |

h3. Conclusions

* The fast path delivers a 1.50x - 1.81x speedup on encrypted clusters
  across all workloads (~40% wall-clock reduction per reopen).
* Group D matches KUDU-3778's reported numbers for the same
  configuration (811 ms -> 394 ms in KUDU-3778 vs 805 ms -> 381 ms
  here), so the non-encryption path is not regressed.
* The streaming-vs-in-memory shape is identical to KUDU-3778:
  streaming time scales with total records, in-memory time only scales
  with the per-record decrypt work that cannot be avoided.
* Absolute speedup on encrypted clusters (~1.7x) is lower than on
  non-encrypted clusters (~2.1x) because per-record decryption work
  has to run on both paths; the fast path can only collapse the
  O(records) preadv() syscalls into one bulk read per container.

> Support encrypted metadata in the log block container in-memory replay path
> ---------------------------------------------------------------------------
>
>                 Key: KUDU-3779
>                 URL: https://issues.apache.org/jira/browse/KUDU-3779
>             Project: Kudu
>          Issue Type: Improvement
>            Reporter: Daojiang Yan
>            Assignee: Daojiang Yan
>            Priority: Minor
>         Attachments: KUDU-3779-bench-logs.tar.gz
>
>
> KUDU-3778 change introducing 
> --log_container_metadata_inmem_replay_threshold_bytes) added an in-memory 
> replay fast path for LogBlockContainerNativeMeta::ProcessRecords(): instead 
> of issuing two preadv() syscalls per record, the entire metadata file is 
> slurped into memory once and ReadablePBContainerFile then reads from a 
> MemoryReadableFile wrapper. This drastically reduces syscalls during the 
> "Reading filesystem" stage of tserver startup on hosts with many containers.
> The current implementation intentionally falls back to the streaming path 
> whenever the metadata file is encrypted (i.e. 
> RandomAccessFile::GetEncryptionHeaderSize() > 0). As a result, encrypted 
> clusters do not benefit from the optimization.
> *Why encrypted metadata is excluded today*
> ReadablePBContainerFile recovers from an incomplete trailing record (typical 
> after a crash mid-write, when the on-disk tail is a file-hole filled with 
> zeros) by relying on a per-slice optimization inside DoDecryptV(): it detects 
> all-zero ciphertext slices and leaves them as zeros in plaintext rather than 
> running them through AES. The PB container parser then sees a zeroed tail and 
> returns Status::Incomplete, which the caller handles by truncating the 
> partial record.
> If we preload the whole payload as a single buffer and serve all subsequent 
> reads from memory, the decryption layer sees one bulk slice instead of 
> per-record slices. The trailing zero region is no longer aligned with a slice 
> boundary, the zero-slice shortcut is defeated, and the file-hole tail 
> decrypts to garbled plaintext. The PB parser would then misclassify a benign 
> crash-truncation as corruption.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to