Search This Blog

Showing posts with label Performance. Show all posts
Showing posts with label Performance. Show all posts

Monday, August 31, 2026

PostgreSQL Performance Tweak

PostgreSQL is renowned for ACID compliance, reliability, and high throughput, but out-of-the-box defaults are conservative. Achieving peak performance requires tuning both the database engine parameters and the underlying Linux operating system kernel.


Part 1: Linux OS Kernel Tuning with Tuned

Modern Linux distributions use tuned to dynamically manage system profiles, CPU governors, and virtual memory parameters without manual, error-prone edits to /etc/sysctl.conf.

1. Install and Enable Tuned

dnf -y install tuned
systemctl enable --now tuned

2. Create a Dedicated PostgreSQL Profile

Default profiles (like virtual-guest or power-saving modes) throttle CPU frequency and mismanage dirty buffer flushing. Create a custom profile tailored for database workloads:

mkdir -p /etc/tuned/postgresql-custom

cat << 'EOF' > /etc/tuned/postgresql-custom/tuned.conf
[main]
summary=Tuned profile optimized for PostgreSQL workloads

[bootloader]
cmdline=transparent_hugepage=never

[cpu]
governor=performance
energy_perf_bias=performance
min_perf_pct=100

[sysctl]
# Reduce swappiness to keep DB pages in RAM
vm.swappiness = 10

# Flush dirty memory pages to disk continuously to avoid checkpoint I/O spikes
vm.dirty_background_ratio = 3
vm.dirty_ratio = 10
vm.dirty_expire_centisecs = 500
vm.dirty_writeback_centisecs = 250

# Memory allocation & networking
vm.overcommit_memory = 0
net.ipv4.tcp_timestamps = 0
EOF

3. Activate the Profile

tuned-adm profile postgresql-custom
tuned-adm active

Part 2: Core PostgreSQL Configuration (postgresql.conf)

1. Memory Allocation

  • shared_buffers: Set to 25% of total system RAM for dedicated database servers.
  • effective_cache_size: Estimate how much memory is available for disk caching by the OS and PostgreSQL (typically set to 50% to 75% of total RAM).
  • work_mem: Memory allocated per sorting operation or complex hash table (start conservatively at 32MB–64MB). Watch logs for temporary file creation:
    work_mem = 64MB
  • maintenance_work_mem: Memory used for maintenance operations such as VACUUM, CREATE INDEX, and foreign key checks. Set to ~10% of system RAM (up to 1GB–2GB):
    maintenance_work_mem = 1GB

2. Structured Logging

PostgreSQL logging is lightweight and vital for identifying slow queries, checkpoint stalls, and temp file writes:

logging_collector = on
log_destination = 'csvlog'
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_min_duration_statement = 250ms
log_temp_files = 0
log_checkpoints = on

Part 3: Schema Design & Query Anti-Patterns

  • Don't Use the DB as a Queue or Cache: Avoid putting ephemeral task queues (like Celery), session data, or frequent heartbeat counters in transactional tables.
  • Split Hot and Cold Columns: If a table contains frequently modified fields (e.g., last_active_at) alongside wide, static data, split the hot column into a separate 1-to-1 table to minimize table bloat and write amplifications.
  • Avoid Large IN (...) Clauses: Passing massive lists inside IN queries prevents efficient index scans; join against a temporary table or use unnest() with an array parameter instead.
  • Beware of Unanchored Wildcards: Queries using ILIKE '%pattern%' cannot use standard B-Tree indexes. Use Trigram (pg_trgm) GIN/GiST indexes for partial text search.

Part 4: Maintenance, Indexing & Replication

1. Monitor Table Bloat and Index Usage

Check for sequential scans and unused indexes using built-in statistics views:

-- Identify tables with heavy sequential scans
SELECT relname, seq_scan, seq_tup_read, idx_scan 
FROM pg_stat_user_tables 
WHERE seq_scan > 0 
ORDER BY seq_tup_read DESC LIMIT 10;

2. Safe Schema Migrations on Large Tables

Adding a column with a default value or adding non-concurrent indexes will take an exclusive table lock (ACCESS EXCLUSIVE), blocking all read and write traffic:

  • Always build indexes concurrently: CREATE INDEX CONCURRENTLY ...
  • Add nullable columns first, populate data in batches, and add constraints subsequently.

3. Backups vs. High Availability

  • pg_dump: Ideal for logical exports and development copies, but becomes too slow for multi-hundred gigabyte databases.
  • Physical Streaming Replication & WAL Archiving: Use tools like pgBackRest or native WAL shipping to maintain byte-for-byte read replicas and enable point-in-time recovery (PITR).
```

Disable .vmem in VMware

If you run VMware Workstation or Fusion on mechanical HDDs or wear-sensitive SSDs, you may have noticed severe disk thrashing caused by background memory paging. VMware default settings often write guest memory pages to persistent .vmem files on the host disk, saturating storage I/O and causing system-wide lag.


The Solution: Force In-RAM Execution

To eliminate .vmem file generation and stop VMware from paging guest RAM to your host storage, add the following configuration block to your VM's .vmx file (or globally in settings.ini / preferences):

# Lock 100% of guest memory into physical host RAM
prefvmx.minVmMemPct = "100"

# Disable dynamic memory trimming back to host
MemTrimRate = "0"

# Prevent creation of the .vmem backing file on host disk
mainMem.useNamedFile = "FALSE"

# Disable redundant memory page sharing scans
sched.mem.pshare.enable = "FALSE"

# Automatically lock the recommended memory allocation size
prefvmx.useRecommendedLockedMemSize = "TRUE"

What Each Parameter Does

  • mainMem.useNamedFile = "FALSE": Stops VMware from allocating a full .vmem disk file matching the guest's RAM capacity.
  • prefvmx.minVmMemPct = "100": Demands that 100% of the virtual machine's provisioned RAM remains pinned in physical host memory rather than being paged out.
  • MemTrimRate = "0": Prevents the hypervisor from constantly unmapping and trimming unused guest memory, avoiding bursty host disk I/O.
  • sched.mem.pshare.enable = "FALSE": Disables transparent page sharing (TPS) scanning, eliminating unnecessary background CPU and disk comparisons.
  • prefvmx.useRecommendedLockedMemSize = "TRUE": Directs VMware to reserve and lock host RAM according to optimal memory sizing guidelines.

Implementation Note

Make sure to shut down your virtual machine completely before editing its .vmx file. Because this configuration locks the guest's entire memory space into physical host RAM, ensure your host has enough free physical memory before launching multiple VMs simultaneously.

VMware Performance Enhancing Tweaks

While VMware Workstation and VMware Fusion provide robust virtualization out of the box, default configurations often incur unnecessary host I/O and CPU overhead. If your host machine has ample RAM and fast NVMe/SSD storage, fine-tuning memory backing, disk controllers, and display scaling can significantly enhance VM responsiveness while reducing wear on your host drive.


Where to Apply These Settings

You can apply these parameters per VM or globally across your hypervisor installation:

  • Per-VM: Append directly to the virtual machine's .vmx configuration file (ensure the VM is completely powered off first).
  • Globally on Windows (VMware Workstation): Add to settings.ini located at:
    C:\ProgramData\VMware\VMware Workstation\settings.ini
  • Globally on macOS (VMware Fusion): Add to the preferences files located at:
    ~/Library/Preferences/VMware Fusion/preferences
    ~/Library/Preferences/VMware Fusion/config

1. Eliminate Host Memory Swap Files (.vmem)

By default, VMware maps VM RAM to a .vmem backing file on the host drive, generating continuous disk writes. If you have sufficient physical RAM to keep the VM fully in memory, disable named backing files:

Windows (VMware Workstation):

mainMem.useNamedFile = "FALSE"

(Note: VMware Player does not honor this flag and forces swap creation.)

macOS / Linux (VMware Fusion):

mainMem.backing = "swap"

2. Optimize Disk Controllers & Enable Virtual SSD Flag

Avoid SATA AHCI controllers in VMware when possible—they impose higher CPU latency and lower throughput than SCSI variants:

  • Use LSI Logic SAS (SCSI) for Windows guests.
  • Use VMware Paravirtual SCSI (PVSCSI) for modern Linux and heavy I/O workloads.

If your host storage is solid-state, explicitly inform the guest OS so it optimizes its I/O scheduler and enables TRIM:

scsi0:0.virtualSSD = 1

3. Memory Trimming & Page Sharing Tweaks

Prevent background memory reclaim mechanisms and page-sharing overhead from interrupting disk and CPU cycles:

# Disable memory trimming back to host
MemTrimRate = "0"

# Disable redundant memory page sharing
sched.mem.pshare.enable = "FALSE"

# Prevent automatic memory scale-down
MemAllowAutoScaleDown = "FALSE"

4. Disable Logging & Snapshots

For stable production or benchmark environments, disabling continuous debug logging and unwanted snapshot overhead preserves write cycles:

# Disable VM debug logs (or redirect via log.filename = "...")
logging = "FALSE"

# Disable snapshots completely (if using full standalone backups)
snapshot.disabled = "TRUE"

5. Strip Unity Mode Overhead (Server VMs)

Unity Mode creates large GuestAppsCache folders and incurs background compositing overhead that is unnecessary on headless or server VMs. Fully disable Unity with the following block:

isolation.tools.unity.disable = "TRUE"
unity.allowCompositingInGuest = "FALSE"
unity.enableLaunchMenu = "FALSE"
unity.showBadges = "FALSE"
unity.showBorders = "FALSE"
unity.wasCapable = "FALSE"

6. Retina & High-DPI Display Optimization (macOS)

Rendering guest OS interfaces at native High-DPI Retina resolutions can heavily burden the host GPU. To standardize scaling and full-screen behavior:

# Disable automatic guest window auto-fit
pref.autoFitGuestToWindow = "FALSE"

# Disable host display scaling passthrough
gui.applyHostDisplayScalingToGuest = "FALSE"

# Choose fullscreen scaling mode: "stretchGuestToHost" or "fitHostToGuest"
pref.autoFitFullScreen = "fitHostToGuest"

7. Completely Disable Suspend

Prevent virtual machines from suspending during a host power-off (Windows, macOS, or Linux) to stop VMware from writing RAM content to disk or active SSH sessions.

suspend.disabled = "TRUE"

Wednesday, August 26, 2026

Turbocharging Nginx on Oracle Cloud ARM: Zero-Copy HTTPS with Kernel TLS (kTLS)

Turbocharging Nginx on Oracle Cloud ARM: Zero-Copy HTTPS with Kernel TLS (kTLS)

If you run high-concurrency web applications, media assets, or content management systems like WordPress on Oracle Cloud Infrastructure (OCI) Ampere Altra (ARM64) instances, you are already benefiting from high core counts and efficient compute.

However, serving static assets over HTTPS standardly incurs an invisible system tax: redundant memory copying and excessive CPU context switches.

By offloading symmetric TLS record processing directly to the Linux kernel via Kernel TLS (kTLS), you unlock true zero-copy sendfile() over HTTPS, slashing CPU overhead and maximizing throughput on Ubuntu 24.04 LTS.


The Hidden Bottleneck of User-Space HTTPS

In a traditional Nginx setup serving encrypted traffic:

[Disk / Page Cache] ──(Copy 1)──> [User Space: Nginx / OpenSSL]
                                           │
                                    (Encrypts Data)
                                           │
[Network Interface (NIC)] <──(Copy 2)── [Kernel Socket Buffer]
  1. The kernel reads requested files from disk or page cache into user-space memory buffers.
  2. OpenSSL in user space encrypts the payload block by block.
  3. Encrypted buffers are copied across the user/kernel space boundary into socket buffers.
  4. The network stack finally transmits the packets over the wire.

Because OpenSSL processes symmetric encryption in user space, the Linux kernel's high-performance sendfile() syscall is disabled for HTTPS. Every static asset—images, cached HTML, CSS, JavaScript, and binaries—triggers double memory copies and CPU cache invalidations.


How kTLS Solves the Problem

Kernel TLS cleanly separates the control plane from the data plane:

  • Handshake (Control Plane): Remains entirely in user space. OpenSSL handles certificate validation, key exchange, and session negotiation.
  • Data Plane (Encryption/Decryption): Once session keys are derived, OpenSSL passes them to the kernel via socket options (setsockopt(..., SOL_TLS, ...)). The socket switches to the tls Upper Layer Protocol (ULP).
[Disk / Page Cache] ──(In-Kernel Zero-Copy sendfile)──> [Kernel Crypto: ARM CE] ──> [NIC]

With kTLS active, Nginx issues a standard sendfile() syscall over HTTPS. The kernel reads directly from page cache, applies symmetric encryption (AES-GCM or ChaCha20-Poly1305) in-kernel using ARMv8 Neoverse N1 Cryptographic Extension instructions, and streams packets directly to the network interface. Zero user-space memory copies.


Prerequisites on Ubuntu 24.04 LTS (ARM64)

  • OS: Ubuntu 24.04 LTS (Kernel 6.8+ with CONFIG_TLS=m enabled by default)
  • Hardware: OCI Ampere Altra (ARMv8.2+ with hardware-accelerated AES/SHA Cryptographic Extensions)
  • Software: Nginx with OpenSSL 3.x

Step-by-Step Implementation & Common Pitfalls

1. Load and Persist the Kernel Module

The tls module is included in Ubuntu 24.04 but must be loaded:

# Load module immediately
sudo modprobe tls

# Persist module across reboots
echo "tls" | sudo tee /etc/modules-load.d/ktls.conf

Verify that the module is loaded:

lsmod | grep tls

2. The Nginx Configuration Pitfall

Many online guides suggest adding this directive to /etc/nginx/nginx.conf:

ssl_conf_cmd Options KTLS;

On standard Ubuntu 24.04 builds, running nginx -t will often throw an error:

[emerg] unknown directive "ssl_conf_cmd" in /etc/nginx/nginx.conf
nginx: configuration file /etc/nginx/nginx.conf test failed

The Fix: Do not place ssl_conf_cmd in Nginx. Instead, configure OpenSSL 3.x system-wide so all Nginx worker processes automatically use kTLS.

3. Enable kTLS via OpenSSL 3.x Configuration

Open /etc/ssl/openssl.cnf:

sudo nano /etc/ssl/openssl.cnf

Step A: Locate the existing [openssl_init] block (around line 43) and add ssl_conf = ssl_sect:

[openssl_init]
providers = provider_sect
ssl_conf = ssl_sect

Step B: Scroll to the very bottom of the file and append the SSL section:

[ssl_sect]
system_default = system_default_sect

[system_default_sect]
CipherString = DEFAULT:@SECLEVEL=2
Options = KTLS

Save and exit the file.

4. Configure Nginx and Restart

Ensure your /etc/nginx/nginx.conf has sendfile and modern TLS protocols enabled:

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # ... remaining server configurations ...
}

Test configuration and restart the service:

sudo nginx -t && sudo systemctl restart nginx

Verifying kTLS in Production

Generate test HTTPS traffic against your server:

curl -k -o /dev/null https://127.0.0.1/

1. Check Kernel TLS Statistics

Inspect the kernel's real-time TLS statistics:

cat /proc/net/tls_stat

Key counters to watch:

  • TlsTxSw: Increments with every TLS record encrypted via software crypto routines in the kernel.
  • TlsCurrTxSw: Shows currently active kernel TLS transmit sockets.

2. Inspect Active Socket Offload

Run ss to verify that active connections use the TLS Upper Layer Protocol:

ss -ti '( sport = :443 or dport = :443 )'

Look for ulp:tls in the socket options output.


Performance Comparison

Metric Standard TLS (User Space) Kernel TLS (kTLS Zero-Copy)
Data Path 2 Buffer Copies (Kernel $\rightarrow$ User $\rightarrow$ Kernel) 0 Copies (Direct Page Cache $\rightarrow$ NIC)
sendfile() Syscall Disabled for HTTPS Fully Supported
CPU Overhead High %usr (User space encryption) Low %usr, shifts cleanly to %sys
Throughput Memory bandwidth bound Line-rate network bound

Summary

Enabling kTLS on Oracle Cloud ARM instances is a zero-cost optimization that yields substantial returns for static file delivery. By fixing the OpenSSL 3.x system configuration on Ubuntu 24.04, you eliminate user-space copy bottlenecks and allow the ARM Neoverse crypto engine to process encrypted traffic at native line rates.

Thursday, May 27, 2021

Suggestion: disable irqbalance service on Virtual Guests (Virtual Machine)

irqbalance is a Linux daemon that distributes interrupts over among the processors and cores in your computer system. The design goal of irqbalance is to do find a balance between power savings and optimal performance. To a large degree, the work irqbalance does is invisible to you. The daemon balances savings in power consumption with performance. On Virtual Guests (Virtual Machine). It does not really make sense because unless you are pinning the guest to specific CPUs and IRQs and dedicated net/storage hardware, you will likely not see the benefits you would on bare metal. On my Debian VM:
mds@debian:~# dpkg -l|grep irq
ii  irqbalance                    1.5.0-3                      amd64        Daemon to balance interrupts for SMP systems
root@download:~# dpkg --purge irqbalance
(Reading database ... 32086 files and directories currently installed.)
Removing irqbalance (1.5.0-3) ...
Purging configuration files for irqbalance (1.5.0-3) ...
Processing triggers for systemd (241-7~deb10u7) ...

Wednesday, March 3, 2021

Delete trillion records from a large table

Try to clean a large table (non partition, over 5 trillion records) on a old Oracle 10g Database. [TYPE_ID] in the PK. First try:

DELETE FROM LARGE_TABLE_T
WHERE TYPE_ID = 199
AND ROWNUM < 1000;

COMMIT;

999 rows deleted. Elapsed: 00:00:11.42		90/second

DELETE FROM LARGE_TABLE_T
WHERE TYPE_ID = 199
AND ROWNUM < 10000;

COMMIT;

9999 rows deleted. Elapsed: 00:00:19.54		526/second

DELETE FROM LARGE_TABLE_T
WHERE TYPE_ID = 199
AND ROWNUM < 100000;

COMMIT;

99999 rows deleted. Elapsed: 00:04:47.26	348/second


DELETE FROM LARGE_TABLE_T
WHERE TYPE_ID = 199
AND ROWNUM < 20000;

COMMIT;

19999 rows deleted. Elapsed: 00:00:40.08	500/second

DELETE FROM LARGE_TABLE_T
WHERE TYPE_ID = 199
AND ROWNUM < 5000;

COMMIT;

4999 rows deleted. Elapsed: 00:00:10.04		499/second


DELETE FROM LARGE_TABLE_T
WHERE TYPE_ID = 199
AND ROWNUM < 9000;

COMMIT;

8999 rows deleted. Elapsed: 00:00:21.52		409/second

DELETE FROM LARGE_TABLE_T
WHERE TYPE_ID = 199
AND ROWNUM < 50000;

COMMIT;

49999 rows deleted. Elapsed: 00:01:51.70	447/second
I wrote a quick loop with commit every 10000 rows:
BEGIN
LOOP 
  DELETE FROM LARGE_TABLE_T WHERE TYPE_ID = 199 AND ROWNUM < 10000;
  COMMIT;
END LOOP;
COMMIT;
END;
Not bad, Eh?
I let it run on the sqlplus from server and keep try different way.

Found another way faster solution after few tweaks:
DECLARE

  CURSOR c_delete_rowids
  IS
	SELECT ROWID
    FROM LARGE_TABLE_T
	WHERE TYPE_ID = 199
	AND ROWNUM <= 1000000;

  v_count number(4) default 0;

BEGIN

  FOR rec IN c_delete_rowids
  LOOP
  
    delete from LARGE_TABLE_T where rowid = rec.rowid;
	v_count := v_count + 1;

	IF v_count >= 1000 then
		v_count := 0;
		COMMIT;
	END IF;

  END LOOP;

  COMMIT;

END;
/
Which delete 1,000,000 records in 1 minute.
SQL> 13:26:21 SQL> 13:26:21 SQL> 13:26:21 SQL> 13:26:21   2  13:26:21   3  13:26:21   4  13:26:21   5  13:26:21   6  13:26:21   7  13:26:21   8  13:26:21   9  13:26:21  10  13:26:21  11  13:26:21  12  13:26:21  13  13:26:21  14  13:26:21  15  13:26:21  16  13:26:21  17  13:26:21  18  13:26:21  19  13:26:21  20  13:26:21  21  13:26:21  22  13:26:21  23  13:26:21  24  13:26:21  25  13:26:21  26  13:26:21  27  13:26:21  28  13:26:21  29  13:26:21  30  13:26:21  31
PL/SQL procedure successfully completed.

Elapsed: 00:01:02.75
To protect the archive log not run crazy, I call it from a shell script and sleep 100 every loop. 1.5 trillion records has been delete around 3 days. Super!!!