vercel / storage

Vercel Postgres, KV, Blob, and Edge Config
https://vercel.com/storage
Apache License 2.0
517 stars 58 forks source link

bug: zrange rev flag causes empty return list / flaky zrange #742

Open benheckmann opened 3 months ago

benheckmann commented 3 months ago

Hi,

When using kv.zrange, the same input sometimes returns the list of values, and sometimes an empty list. When using the cli on Vercel, I always get the list.

export async function getChats(userId?: string | null) {
  const session = await auth()

  if (!userId) {
    return []
  }

  if (userId !== session?.user?.id) {
    return {
      error: 'Unauthorized'
    }
  }

  try {
    const chats: string[] = await kv.zrange(`user:chat:${userId}`, 0, -1, {
      rev: true
    })

    const pipeline = kv.pipeline()
    for (const chat of chats) {
      pipeline.hgetall(chat)
    }
    const results = await pipeline.exec()

    return results as Chat[]
  } catch (error) {
    return []
  }
}

I have used the debugger to make sure that the userId is the same in both cases and it passes the authentication check. However, chats sometimes evaluates to an empty list, when it should have the values I get using the cli. This is right below the line, so its not that there is an error thrown.

Not sure if this is an issue with the package or if I'm just unable to find out what the difference in the calls is when it works. In this case: is it possible to activate some sort of logging?

benheckmann commented 3 months ago

Update: I have done some more tests and find that the {rev: true} option causes this specific issue.

Here are some examples of what the cli returns vs what the node sdk returns.

CLI:

➜ zrange user:chat:1 0 -1 rev
chat:C9Osv8r, chat:v3XkExq, chat:yjFBYAv, chat:oSSCC6g, chat:GLcQnjN, chat:cCmsDo5, chat:2WR6JPL, chat:SPPpe8H, chat:fvgQqRz, chat:qDL4ITh, chat:kAlLWwR, chat:e13tXFh
➜ zrange user:chat:29d2f72f-46a9-4137-9198-436f3194f64c 0 -1 rev
chat:Ax30Fwb, chat:IhvC1Na, chat:Ph9PXVN, chat:aaPtIoD
➜ zrange user:chat:1 0 -1
chat:e13tXFh, chat:kAlLWwR, chat:qDL4ITh, chat:fvgQqRz, chat:SPPpe8H, chat:2WR6JPL, chat:cCmsDo5, chat:GLcQnjN, chat:oSSCC6g, chat:yjFBYAv, chat:v3XkExq, chat:C9Osv8r
➜ zrange user:chat:29d2f72f-46a9-4137-9198-436f3194f64c 0 -1
chat:aaPtIoD, chat:Ph9PXVN, chat:IhvC1Na, chat:Ax30Fwb

Node:

const key1 = "user:chat:1"
const key2 = "user:chat:29d2f72f-46a9-4137-9198-436f3194f64c"
const chats1: string[] = await kv.zrange(key1, 0, -1, { rev: true }) // returns Array(0) >> but should be returning Array(12)
const chats2: string[] = await kv.zrange(key2, 0, -1, { rev: true }) // returns Array(4)
const chats1NoRev: string[] = await kv.zrange(key1, 0, -1) // returns Array(12)
const chats2NoRev: string[] = await kv.zrange(key2, 0, -1) // returns Array(4)

const keyExists = await kv.exists(key1) // returns 1
const keyType = await kv.type(key1) // returns zset
const setSize = await kv.zcard(key1) // returns 12

For now, I will just omit the rev option and reverse the list myself.

benheckmann commented 3 months ago

I have done some more tests and keep running into situations with unexpected behavior. For instance,

const chats: string[] = await kv.zrange(`user:chat:${userId}`, 0, -1)

sometimes returns an empty array, while

const chats: string[] = await kv.zrange(`user:chat:${userId}`, 0, 3)

is not empty.