Skip to content
Merged
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
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,17 @@ Should return `200` with info about blind review for this case.
#### 6. API Documentation

If you are able to access the api via a web browser (e.g., over VPN inside a vnet or if the app is exposed) you may find it useful to read the Swagger API documentation at `<api_host>/api/v1/docs`.

## Development

### Setup

Use `uv` for installing dependencies and running the application.

### Testing

Use `pytest` to run tests: `uv run pytest`.

> [!NOTE]
> In order to run the tests correctly you will need to pull test files with [`git-lfs`](https://git-lfs.com/).
> Install `git-lfs` and then run `git lfs pull` in this directory to fetch the files.
23 changes: 17 additions & 6 deletions app/server/store/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,20 +154,25 @@ async def dequeue(self, key: str) -> bytes | None:

async def set(self, key: str, value: str | bytes):
k = self._key_func(key)
self.pipe.set(k, value) # type: ignore[attr-defined]
self.pipe.set(k, value or b"") # type: ignore[attr-defined]

async def sadd(self, key: str, *value):
k = self._key_func(key)
self.pipe.sadd(k, *value) # type: ignore[attr-defined]
clean_values = [v or b"" for v in value]
self.pipe.sadd(k, *clean_values) # type: ignore[attr-defined]

async def expire_at(self, key: str, expire_at: int):
k = self._key_func(key)
logger.debug("Redis cluster expireat %r -> %r", k, expire_at)
self.pipe.expireat(k, expire_at) # type: ignore[attr-defined]

async def hsetmapping(self, key: str, mapping: SimpleMapping):
# The redis library fails if there's an empty mapping, so avoid this case.
if not mapping:
return
Comment on lines +170 to +172

This comment was marked as outdated.

k = self._key_func(key)
self.pipe.hset(k, mapping=dict(mapping)) # type: ignore[attr-defined]
clean_mapping = {k: v or b"" for k, v in dict(mapping).items()}
self.pipe.hset(k, mapping=clean_mapping) # type: ignore[attr-defined]


class BaseSimpleRedisStoreSession(StoreSession):
Expand All @@ -194,7 +199,7 @@ async def close(self):
await self.client.aclose(close_connection_pool=False)

async def set(self, key: str, value: str | bytes):
await self.pipe.set(key, value)
await self.pipe.set(key, value or b"")

async def get(self, key: str) -> bytes | None:
# TODO(jnu): We can't watch a key in the middle of a pipeline.
Expand All @@ -221,7 +226,10 @@ async def hsetmapping(self, key: str, mapping: SimpleMapping):
# NOTE: using a `dict` call here since our typings are a little
# more broad than the redis library technically accepts. This should
# really be a no-op in most cases.
await _maybe_wait(self.pipe.hset(key, mapping=dict(mapping)))
if not mapping:
return
clean_mapping = {k: v or b"" for k, v in dict(mapping).items()}
await _maybe_wait(self.pipe.hset(key, mapping=clean_mapping))

async def expire_at(self, key: str, expire_at: int):
await self.pipe.expireat(key, expire_at)
Expand All @@ -230,7 +238,10 @@ async def enqueue(self, key: str, value: str):
await _maybe_wait(self.pipe.lpush(key, value))

async def sadd(self, key: str, *value):
await _maybe_wait(self.pipe.sadd(key, *value))
if not value:
return
clean_values = [v or b"" for v in value]
await _maybe_wait(self.pipe.sadd(key, *clean_values))


class RedisStoreSession(BaseSimpleRedisStoreSession):
Expand Down