跳到正文
cccskills
免费在 GitHub 公开

clickhouse-nip33-addressable-dedup

Fix duplicate Nostr events in ClickHouse when using ReplacingMergeTree for NIP-33 addressable events (Kind 30000+). Use when: (1) Edited videos/events appear as duplicates, (2) Same d_tag shows multiple events with different IDs, (3) FINAL keyword doesn't deduplicate properly for parameterized replaceable events. The issue is that FINAL deduplicates by ORDER BY key (typically `id`), not by (pubkey, kind, d_tag).

中文简介正在准备中。当前显示原文说明。

查看安装方法

包含的文件(1)

  • SKILL.md4.3 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

ClickHouse NIP-33 Addressable Event Deduplication

Problem

When storing Nostr events in ClickHouse using ReplacingMergeTree, edited addressable events (Kind 30000-39999) appear as duplicates. Users edit their video/event, a new event ID is created with the same d_tag, but both versions are shown instead of just the latest.

Context / Trigger Conditions

  • Nostr relay storing events in ClickHouse with ReplacingMergeTree
  • Users report seeing duplicate videos/events after editing
  • Query returns multiple events with same (pubkey, kind, d_tag) but different id values
  • Using FINAL keyword but duplicates still appear
  • Kind 30000+ events (NIP-33 parameterized replaceable events like Kind 34236 videos)

Root Cause

The FINAL keyword in ClickHouse deduplicates based on the table's ORDER BY key. If your table is defined as:

ENGINE = ReplacingMergeTree(indexed_at)
ORDER BY (id)

Then FINAL deduplicates by id. Two events with different IDs are NOT considered duplicates, even if they represent the same addressable "slot" per NIP-33.

For NIP-33 addressable events, the replacement key should be (pubkey, kind, d_tag), not id.

Solution

Option 1: Fix at View Level (Recommended)

Change your videos view to use LIMIT 1 BY instead of FINAL:

CREATE VIEW videos AS
SELECT
    id,
    pubkey,
    created_at,
    kind,
    content,
    tags,
    d_tag,
    title,
    thumbnail,
    video_url
FROM events_local
WHERE kind IN (34235, 34236)
ORDER BY pubkey, kind, d_tag, created_at DESC
LIMIT 1 BY pubkey, kind, d_tag;

The LIMIT 1 BY clause keeps only the first row (latest by created_at) for each unique combination of (pubkey, kind, d_tag).

Option 2: Fix at Table Level (Breaking Change)

If you can recreate the table, use a composite ORDER BY:

CREATE TABLE events_addressable (
    ...
) ENGINE = ReplacingMergeTree(created_at)
ORDER BY (pubkey, kind, d_tag);

This makes FINAL work correctly for NIP-33 events but may not work for all event types.

Migration Example

-- Drop dependent views first
DROP VIEW IF EXISTS trending_videos;
DROP VIEW IF EXISTS video_stats;
DROP VIEW IF EXISTS videos;

-- Recreate with proper deduplication
CREATE VIEW videos AS
SELECT *
FROM events_local
WHERE kind IN (34235, 34236)
ORDER BY pubkey, kind, d_tag, created_at DESC
LIMIT 1 BY pubkey, kind, d_tag;

-- Recreate dependent views...

Verification

Query for a specific user's videos and confirm no duplicates:

SELECT id, d_tag, created_at
FROM videos
WHERE pubkey = 'user_pubkey_here'
ORDER BY created_at DESC;

Each d_tag should appear only once, with the highest created_at value.

Example

Before (broken):

| id       | d_tag    | created_at |
|----------|----------|------------|
| abc123   | video1   | 1769697188 | ← Newer edit
| def456   | video1   | 1769697150 | ← Original (should be hidden)

After (fixed):

| id       | d_tag    | created_at |
|----------|----------|------------|
| abc123   | video1   | 1769697188 | ← Only latest shown

Notes

  • This applies to all NIP-33 addressable events (Kind 30000-39999), not just videos
  • The LIMIT 1 BY approach is query-time deduplication, not storage deduplication
  • Old event versions remain in storage but won't appear in query results
  • Consider periodic cleanup of old event versions if storage is a concern
  • Don't forget to recreate dependent views in the correct order

References

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Fix ArgoCD ExternalSecret deployment failing with "namespace X is not permitted in project Y". Use when: (1) ExternalSecret shows OutOfSync in ArgoCD but won't sync, (2) ArgoCD application status shows "namespace X is not permitted in project 'infrastructure'", (3) ExternalSecret targets a namespace managed by a different ArgoCD project, (4) Using apps-of-apps pattern with separate infrastructure and application projects.

中文简介正在准备中。当前显示原文说明。

divinevideo/divine-mobile2652026年10月23日 更新

Art direction for any content — reads text, PDF, Word, HTML, PPT, then proposes 2-3 creative directions with photography style, mood, and visual language. After selection, generates AI image prompts and visual briefs section-by-section. Use when the user shares content and needs visual direction, image sourcing, or creative direction for any material.

中文简介正在准备中。当前显示原文说明。

divinevideo/divine-mobile2652026年10月23日 更新

Fix "Null check operator used on a null value" errors when an object is set to null during an async await. Use when: (1) Object reference is nullified while awaiting, (2) Code accesses object with ! after await returns, (3) Cancel/dispose operations run concurrently with async operations on same object. Solution: capture local reference before await.

中文简介正在准备中。当前显示原文说明。

divinevideo/divine-mobile2652026年10月23日 更新

Add custom metadata headers (x-amz-meta-*) to AWS v4 signed requests for GCS S3-compatible API. Use when: (1) Adding custom metadata to GCS uploads via S3 API, (2) Getting signature mismatch errors after adding new headers, (3) x-amz-meta-* headers being ignored or causing 403 errors. Custom headers MUST be included in canonical headers and signed headers list.

中文简介正在准备中。当前显示原文说明。

divinevideo/divine-mobile2652026年10月23日 更新

Fix password/secret authentication failures caused by trailing newlines when creating Google Cloud secrets (or similar) with bash here-strings. Use when: (1) Password authentication fails with correct password, (2) Secret created with `<<< "value"` syntax, (3) Error like "password authentication failed" or "invalid token" despite correct value. Bash here-strings (`<<<`) add a trailing newline that corrupts secrets.

中文简介正在准备中。当前显示原文说明。

divinevideo/divine-mobile2652026年10月23日 更新

Fix silent video/media processing failures caused by URL extraction code that filters on file extensions (.mp4, .webm, .webp). Use when: (1) Media moderation, transcoding, or analysis silently skips files from Blossom or content-addressed storage servers, (2) URL extraction from Nostr event tags (imeta, r tags) drops URLs without recognized extensions, (3) CDN fallback URLs append .mp4 but the actual server uses extensionless content-addressed paths like /{sha256}. Common in Nostr video events (kind 34236) where different clients use different URL formats.

中文简介正在准备中。当前显示原文说明。

divinevideo/divine-mobile2652026年10月23日 更新

查看 divinevideo 的全部技能

报告此技能的问题