Deserialising cursor fails when based solely on a timestamp column
scottmatthewman opened this issue · 0 comments
I have a potentially large table whose ID will be a UUID (so I'm using the master
branch of activerecord_cursor_paginate
to make use of append_primary_key: false
so that the cursor is based solely on created_at
.
In my API's index method, I call
paginator = SubmittedUrl.cursor_paginate(
order: { created_at: :desc },
after: params[:after],
append_primary_key: false
)
page = paginator.fetch
The first page of results is fine, and it correctly gives me the page.next_cursor
for me to include in the response. However, when I pass that cursor into another request as my :after
param, everything explodes.
ActiveRecordCursorPaginate::InvalidCursorError:
The given cursor `IjBhSVgyXzE3MTU1MTc0OTA1ODM0MTYi` could not be decoded
# /usr/local/bundle/bundler/gems/activerecord_cursor_paginate-0c262804e436/lib/activerecord_cursor_paginate/cursor.rb:34:in `rescue in decode'
# /usr/local/bundle/bundler/gems/activerecord_cursor_paginate-0c262804e436/lib/activerecord_cursor_paginate/cursor.rb:16:in `decode'
# /usr/local/bundle/bundler/gems/activerecord_cursor_paginate-0c262804e436/lib/activerecord_cursor_paginate/paginator.rb:96:in `fetch'
Upon examination, the issue is in the initialize
method of ActiveRecordCursorPaginate
.
A cursor value of 0aIX2_1715517083528549
correctly decodes to 2024-05-12 12:31:23.528549 UTC
, which is created as a Time
object. But when a Time
instance is passed into Array(args)
, Ruby splits the object into multiple components (using Time#to_a
):
time = Time.now.utc
# => 2024-05-12 13:01:14.646513237 UTC
Array(time)
# => [14, 1, 13, 12, 5, 2024, 0, 133, false, "UTC"]
This means that the values and columns no longer match.
A remedy is to use [values].flatten
instead, which keeps the Time
object intact. This construction would be destructive for arrays of arrays (e.g., [[:a, :b], [:c, :d]]
would resolve to [:a, :b, :c, :d]
) but that shouldn't be a problem here.