我的笔记现在会自动发到 X —— 靠一个只读 Redis 旁观者

我有一个手动发推的 CLI(Playwright 驱动登录状态的 Firefox 网页,所以不需要 X API key),还有一个多用户笔记应用(HappyNotes)把新笔记推入 Redis 队列同步到 Mastodon。想让它俩连起来:笔记一发,自动发推。但不想动共享后端、毕竟这是一个很私人的hack,也没法子支持其他用户。

演进三步

1. CLI → 微服务。 把发推核心抽成库,Node 内置 http 起一个服务,只监听 Tailscale IP(100.x.y.z:8090)——内网即鉴权,零依赖、零鉴权代码。任何 tailnet 机器都能入队:

curl -X POST http://100.x.y.z:8090/add \
  -H 'content-type: application/json' \
  -d '{"text":"hello","post_at":"2026-09-07T09:00:00+12:00"}'

2. 图片走 data URL。 别的机器没有你这台的文件系统,multipart 又要新依赖——所以图片在 JSON body 里以 data:image/png;base64,... 传输,Playwright setInputFiles 喂内存 buffer,最多 4 张。

3. 只读 Redis 旁观者。 这是关键设计:不 LPOP(会偷走 Mastodon 消费者的消息),只每秒 LRANGE queue + ZRANGEBYSCORE processing,用 redis-cli 子进程(还是零新依赖)。然后三道过滤:

userId == OWNER_USER_ID  &&  action == "CREATE"  &&  isPrivate == false

task.id 落盘去重(先写 seen 再发布,失败绝不重试——重试 = 重复推文),冷启动基线首轮只记已见不发布,防止积压刷屏。

代价:一个有意的竞态窗口

队列项最多活 5 秒(消费者 LPOP 后成功后 ZREM,删了就没了,没有 deleted 队列可拣漏)。所以旁观者"几乎不漏但不保证"——如果任务在两次轮询之间被消费完,就错过了。要修复的话要动HappyNotes后端(加一个 synced 队列,成功时 LPUSH + 1 小时 TTL),但只为我一个人需求改全站代码不太值,先接受这个妥协看看。

MY AI TEAM is released on LemonSqueezy now - special discount (90% off) for my blog readers

My ai team help you ship features when you sleep. To celebrate its release, I created this discount code for my readers.

CXMJEWMQ 90% OFF - Only for Personal version.

https://mat.shukelabs.com

This discount code can be used for Personal variant only.

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