Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/changelog/historical-tids
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Backfilled posts can now publish with rkeys that reflect the original publish date.
70 changes: 70 additions & 0 deletions includes/transformer/class-base.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,76 @@ public function get_uri(): string {
);
}

/**
* Mint an rkey that honors the historical-TID filter contract.
*
* Shared by `Post::get_rkey()`, `Document::get_rkey()`, and
* `Comment::get_rkey()`. Each subclass calls this with its own
* collection NSID, the relevant `*_date_gmt` string and ID, a
* per-class salt prefix (so `applyWrites` can never see two records
* collapse onto the same rkey within a single collection), and an
* optional kind label that separates records of different WordPress
* object kinds when they share an AT Protocol collection.
*
* The historical path falls back to `TID::generate()` when either
* the filter opts out or the GMT datetime cannot be parsed
* (empty string, MySQL `0000-00-00 00:00:00` sentinel, garbage),
* so callers never need to handle the "no usable date" case.
*
* @since unreleased
*
* @param string $collection AT Protocol collection NSID.
* @param string $gmt_datetime GMT datetime string from the WP object.
* @param int $object_id Post or comment identifier.
* @param string $salt_prefix Per-class salt prefix (e.g. `'post:'`).
* @param \WP_Post|\WP_Comment $wp_object WordPress object being transformed.
* @param string $kind Optional kind label for collection-sharing
* record types (e.g. `'comment'`).
* @return string Minted TID rkey.
*/
final protected function mint_historical_rkey(
string $collection,
string $gmt_datetime,
int $object_id,
string $salt_prefix,
\WP_Post|\WP_Comment $wp_object,
string $kind = ''
): string {
/**
* Filters whether to mint a historical TID based on the post's
* original publish date.
*
* Defaults to true so backfilled posts get rkeys that reflect
* the original publish time rather than the time of the
* backfill run. Return false to fall back to the live-publish
* `TID::generate()` path (now-based, with the monotonic floor).
*
* @since unreleased
*
* @param bool $use_historical Whether to mint a historical TID.
* @param \WP_Post|\WP_Comment $object WordPress object being transformed.
* @param string $collection AT Protocol collection NSID.
*/
$use_historical = (bool) \apply_filters(
'atmosphere_use_historical_tid',
true,
$wp_object,
$collection
);

if ( ! $use_historical ) {
return TID::generate();
}

$microseconds = TID::microseconds_from_post_date( $gmt_datetime, $object_id, $kind );

if ( $microseconds <= 0 ) {
return TID::generate();
}

return TID::generate_for_time( $microseconds, $salt_prefix . $object_id );
}

/**
* WordPress locale as BCP-47 language tag array.
*
Expand Down
36 changes: 35 additions & 1 deletion includes/transformer/class-comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,33 @@
*/
class Comment extends Base {

/**
* Salt prefix passed to {@see TID::generate_for_time()} for comments.
*
* Combined with the comment ID it produces the deterministic
* clock-id input for the historical TID path.
*
* @since unreleased
*
* @var string
*/
public const TID_SALT_PREFIX = 'comment:';

/**
* Kind label passed to {@see TID::microseconds_from_post_date()}.
*
* Comments share the `app.bsky.feed.post` collection with posts, so
* a post-id-vs-comment-id collision inside a single GMT second would
* otherwise mint identical rkeys. The kind label folds an extra
* deterministic offset into the microsecond portion so posts and
* comments occupy different sub-second windows.
*
* @since unreleased
*
* @var string
*/
public const TID_KIND = 'comment';

/**
* Comment meta key for the bsky post TID (rkey).
*
Expand Down Expand Up @@ -139,7 +166,14 @@ public function get_rkey(): string {
$rkey = \get_comment_meta( (int) $this->object->comment_ID, self::META_TID, true );

if ( empty( $rkey ) ) {
$rkey = TID::generate();
$rkey = $this->mint_historical_rkey(
$this->get_collection(),
(string) $this->object->comment_date_gmt,
(int) $this->object->comment_ID,
self::TID_SALT_PREFIX,
$this->object,
self::TID_KIND
);
\update_comment_meta( (int) $this->object->comment_ID, self::META_TID, $rkey );
}

Expand Down
22 changes: 21 additions & 1 deletion includes/transformer/class-document.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@
*/
class Document extends Base {

/**
* Salt prefix passed to {@see TID::generate_for_time()} for documents.
*
* Combined with the post ID it produces the deterministic clock-id
* input for the historical TID path; namespacing by class keeps
* `Document` and `Post` rkeys distinct even though both derive from
* the same `WP_Post`.
*
* @since unreleased
*
* @var string
*/
public const TID_SALT_PREFIX = 'document:';

/**
* Post meta key for the document TID.
*
Expand Down Expand Up @@ -199,7 +213,13 @@ public function get_rkey(): string {
$rkey = \get_post_meta( $this->object->ID, self::META_TID, true );

if ( empty( $rkey ) ) {
$rkey = TID::generate();
$rkey = $this->mint_historical_rkey(
$this->get_collection(),
(string) $this->object->post_date_gmt,
(int) $this->object->ID,
self::TID_SALT_PREFIX,
$this->object
);
\update_post_meta( $this->object->ID, self::META_TID, $rkey );
}

Expand Down
22 changes: 21 additions & 1 deletion includes/transformer/class-post.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@
*/
class Post extends Base {

/**
* Salt prefix passed to {@see TID::generate_for_time()} for posts.
*
* Combined with the post ID it produces the deterministic clock-id
* input for the historical TID path; namespacing by class keeps
* `Post` and `Document` rkeys distinct even though both derive from
* the same `WP_Post`.
*
* @since unreleased
*
* @var string
*/
public const TID_SALT_PREFIX = 'post:';

/**
* Post meta key for the bsky post TID.
*
Expand Down Expand Up @@ -277,7 +291,13 @@ public function get_rkey(): string {
$rkey = \get_post_meta( $this->object->ID, self::META_TID, true );

if ( empty( $rkey ) ) {
$rkey = TID::generate();
$rkey = $this->mint_historical_rkey(
$this->get_collection(),
(string) $this->object->post_date_gmt,
(int) $this->object->ID,
self::TID_SALT_PREFIX,
$this->object
);
\update_post_meta( $this->object->ID, self::META_TID, $rkey );
}

Expand Down
Loading