Oracle `FOR UPDATE`: the timeout belongs to the waiter, not the lock holder

You'll run into this shape in PL/SQL procedures that need to serialize work per row:

SELECT clientid
  INTO l_locked_client_id
  FROM dataela.clients
 WHERE clientid = p_client_id
 FOR UPDATE;

The selected value is never used — the variable name admits it. The query exists purely to take a row-level exclusive lock, turning that client row into a mutex so a read-modify-write sequence can't be clobbered by a concurrent session. It raises NO_DATA_FOUND for free if the client doesn't exist.

Two things about that lock are easy to get wrong.

The lock outlives the procedure. A PL/SQL block is not a transaction boundary. When the procedure returns, the lock is still held — it belongs to the caller's transaction and is released only at COMMIT or ROLLBACK (or when the session dies, or on the implicit commit from a stray DDL statement mid-transaction, which drops it silently). So hold time is decided by the caller, not by the procedure that took the lock. If the caller runs for five minutes, the row is locked for five minutes. Worth a comment on the procedure saying exactly that.

The WAIT clause is the waiter's patience, not the holder's timeout.

FOR UPDATE              -- wait forever (default)
FOR UPDATE NOWAIT       -- fail immediately, ORA-00054
FOR UPDATE WAIT 5       -- give up after 5s, ORA-30006
FOR UPDATE SKIP LOCKED  -- skip locked rows (queue consumers)

Each clause constrains only the statement it's attached to, so whatever the holder wrote has no effect on how long anyone else waits. Oracle has no holder-side "release after N seconds" and no global lock timeout to protect you — if you don't want connections piling up behind a lock, every call site that might wait needs its own NOWAIT or WAIT n plus a handler that retries or returns "try again later". Breaking a lock from outside means ALTER SYSTEM KILL SESSION.

That default infinite wait is also what makes the pattern deadlock-prone: two procedures locking several client rows in opposite orders will wait on each other until ORA-00060. Lock in a consistent order, or use NOWAIT with a retry.

One thing you don't have to worry about: a plain SELECT never blocks on any of this. Oracle rebuilds the pre-change version of the row from undo and reads that, so reporting queries pass straight through a locked row — none of the WITH (NOLOCK) reflex SQL Server teaches. Only FOR UPDATE, UPDATE, and DELETE against the same row queue up behind you, and only that row.

Your RDP session can't wake the physical monitors, and that's not a bug

I run a lid-closed laptop with two external monitors as a remote-access host. Remote desktop into it works perfectly, but the physical monitors sitting on my desk were pitch black. Cables fine, monitors powered, nothing in the logs.

The screens weren't broken — they were just DPMS-blanked, and nothing I did remotely could wake them:

$ sudo XAUTHORITY=/var/run/lightdm/root/:0 DISPLAY=:0 xset q
DPMS (Display Power Management Signaling):
  Standby: 600    Suspend: 0    Off: 900
  DPMS is Enabled
  Monitor is Off

One command brings them back:

sudo XAUTHORITY=/var/run/lightdm/root/:0 DISPLAY=:0 xset dpms force on

The reason it happened at all is the part worth remembering: xrdp gives you a second X server. The console session is Xorg :0; your RDP session is Xorg :10. They're separate processes with separate idle timers. Every keystroke and mouse move you make remotely resets the idle timer on :10 — and :0 never hears about any of it. So :0 sits there accumulating idle time forever and blanks on schedule, no matter how busy you look on the other display.

If the host is supposed to stay lit, disable blanking in the console user's session, not yours. On XFCE:

xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/dpms-enabled -s false
for k in blank-on-ac blank-on-battery dpms-on-ac-sleep dpms-on-ac-off \
         dpms-on-battery-sleep dpms-on-battery-off; do
  xfconf-query -c xfce4-power-manager -p /xfce4-power-manager/$k -s 0
done

Those only take effect when xfce4-power-manager restarts, so log the console user out and back in. Note the settings persist to ~/.config/xfce4/xfconf/xfce-perchannel-xml/, not ~/.config/xfconf/ — I looked in the wrong place first.

One trap after all that: I logged back in, confirmed DPMS is Disabled, and declared victory too early. xset q still read timeout: 600. That's X's own built-in screensaver, a completely separate mechanism that the power manager's blank-on-ac=0 never touched. DPMS controls whether the monitor gets powered down; the X screensaver just paints over the screen while it stays on. From a chair three feet away the two are indistinguishable, and remote input can't dismiss either one. Kill both, from the console user's autostart:

xset s off s noblank -dpms

Check for timeout: 0 and DPMS is Disabled before you believe it.

Wifi card vanishing from the PCIe bus? Stop rebooting, make it self-heal

On a Toshiba CB35-3340 (Bay Trail Chromebook running MX Linux / Debian 13 on MrChromebox firmware), the internal Intel Wireless 7260 disappears every few hours. The SSID list goes empty, every scan fails with -EIO, and toggling wifi does nothing. Only a reboot brought it back.

The giveaway is in the kernel log:

iwlwifi 0000:01:00.0: iwlwifi device memory mapped registers:
iwlwifi 0000:01:00.0: 00000000: ffffffff ffffffff ffffffff ffffffff
WARNING: ... __iwl_trans_pcie_grab_nic_access+0x14c/0x150 [iwlwifi]
iwlwifi 0000:01:00.0: Error sending STATISTICS_CMD: enqueue_hcmd failed: -5

MMIO reads returning all-ones means the device is off the bus, not merely confused. That is why modprobe -r iwlwifi && modprobe iwlwifi fails with Could not load the [0] uCode section — there is nothing there to load firmware into.

Two things fix the recovery path. First, let the driver admit the device is gone:

…more

`ls` says the group has rwx, but access is denied — that's the ACL mask lying to you

I wanted a second local account to read /home/davidw. The user was already in group davidw, and ls looked like it should just work:

$ ls -ld /home/davidw
drwxrwx---+ 272 davidw davidw 16384 Aug 19 16:56 /home/davidw

Group davidw has rwx, right there in the middle. Except every access failed:

$ sudo -u desk ls /home/davidw
ls: cannot open directory '/home/davidw': Permission denied

The + at the end of the mode string is the tell. Once a file has a POSIX ACL, the middle triad printed by ls stops being the group permission and becomes the ACL mask — a ceiling on what every named user, named group, and the owning group may get. The real group entry is only visible via getfacl:

$ getfacl -p /home/davidw
user::rwx
user:libvirt-qemu:--x
group::---          <-- the actual group permission: nothing
mask::rwx           <-- this is what ls printed as "rwx"
other::---

So the directory was 700 all along. Someone (libvirt, in my case) added user:libvirt-qemu:--x at some point, and adding any named entry forces a mask into existence — set wide enough to cover that entry. ls dutifully displayed the mask, and it looked exactly like group access.

The mask cuts both ways: it can also revoke access that getfacl seems to grant. An entry of user:desk:rwx under mask::r-x yields read-only — getfacl marks it for you:

user:desk:rwx                   #effective:r-x

Two consequences worth internalizing. First, on any path with a +, getfacl is the only source of truth; don't reason from ls. Second, chmod g+rx on an ACL'd file doesn't set the group permission — it sets the mask, which is almost never what you meant. Grant access with a named entry instead, which is narrower and trivially reversible:

sudo setfacl -m u:desk:rwx /home/davidw   # grant
sudo setfacl -x u:desk     /home/davidw   # revert

One unrelated snag if you're doing this to share a checkout: git will refuse the now-reachable repos with detected dubious ownership in repository, because they're owned by a different uid. Fix it per-user, not with sudo:

sudo -u desk git config --global --add safe.directory '*'

Git Bash and MSYS2 disagree on Git SSL? Use one Git for Windows

Git Bash and MSYS2 can load different Git executables while sharing the same ~/.gitconfig. http.sslbackend=schannel works with Git for Windows, but an MSYS2-native Git may support only OpenSSL and fail with Unsupported SSL backend.

Remove the MSYS2 Git package and put Git for Windows first in MSYS2's PATH:

pacman -R --noconfirm git
printf '%s\n' 'export PATH="/c/Program Files/Git/cmd:$PATH"' >> ~/.bashrc
source ~/.bashrc
hash -r

Use one shared configuration:

git config --global http.sslbackend schannel
git config --global credential.helper manager

Verify both shells resolve the same executable:

type -a git
git --version
git ls-remote origin HEAD

This keeps certificate verification in the Windows certificate store and avoids shell-specific OpenSSL CA or stale credential-store differences. Do not disable TLS verification with http.sslVerify=false.