A from-scratch atproto PDS implementation in Python (mirrors https://github.com/DavidBuchanan314/millipds)

treat empty-string cursors the same as nonexistent

+10 -6
+3 -1
src/millipds/atproto_repo.py
··· 220 if limit < 1 or limit > 100: 221 raise web.HTTPBadRequest(text="limit out of range") 222 reverse = request.query.get("reverse") == "true" 223 - cursor = request.query.get("cursor", "" if reverse else "\xff") 224 did_or_handle = request.query["repo"] 225 collection = request.query["collection"] 226 records = []
··· 220 if limit < 1 or limit > 100: 221 raise web.HTTPBadRequest(text="limit out of range") 222 reverse = request.query.get("reverse") == "true" 223 + cursor = request.query.get("cursor", "") 224 + if not reverse and not cursor: 225 + cursor = "\xff" # compares greater than all valid rkeys 226 did_or_handle = request.query["repo"] 227 collection = request.query["collection"] 228 records = []
+3 -3
src/millipds/atproto_sync.py
··· 190 limit = int(request.query.get("limit", 500)) 191 if limit < 1 or limit > 1000: 192 raise web.HTTPBadRequest(text="limit out of range") 193 - cursor = int(request.query.get("cursor", 0)) 194 195 cids = [] 196 for id_, cid in get_db(request).con.execute( ··· 246 await ws.prepare(request) # TODO: should this be outside of try? 247 248 last_sent_seq = None 249 - if "cursor" in request.query: 250 # TODO: try to limit number of concurrent backfillers? (it's more expensive than livetailing) 251 - cursor = int(request.query["cursor"]) 252 db = get_db(request) 253 while True: 254 # we read one at a time to force gaps between reads - could be a perf win to read in small batches
··· 190 limit = int(request.query.get("limit", 500)) 191 if limit < 1 or limit > 1000: 192 raise web.HTTPBadRequest(text="limit out of range") 193 + cursor = int(request.query.get("cursor") or 0) 194 195 cids = [] 196 for id_, cid in get_db(request).con.execute( ··· 246 await ws.prepare(request) # TODO: should this be outside of try? 247 248 last_sent_seq = None 249 + if cursor_str := request.query.get("cursor"): 250 # TODO: try to limit number of concurrent backfillers? (it's more expensive than livetailing) 251 + cursor = int(cursor_str) 252 db = get_db(request) 253 while True: 254 # we read one at a time to force gaps between reads - could be a perf win to read in small batches
+1
src/millipds/database.py
··· 150 ) STRICT 151 """ 152 ) 153 154 # repo storage stuff 155 self.con.execute(
··· 150 ) STRICT 151 """ 152 ) 153 + # TODO: index on timestamp for efficient purging of old events. 154 155 # repo storage stuff 156 self.con.execute(
+3 -2
tests/integration_test.py
··· 494 ) as r: 495 assert r.status != 200 496 497 async def test_updateHandle(s, pds_host, auth_headers): 498 async with s.post( 499 pds_host + "/xrpc/com.atproto.identity.updateHandle", 500 headers=auth_headers, 501 - json={ "handle": "juliet.test" }, 502 ) as r: 503 assert r.status == 200 504 505 async with s.get( 506 pds_host + "/xrpc/com.atproto.repo.describeRepo", 507 - params={ "repo": TEST_DID }, 508 ) as r: 509 assert r.status == 200 510 r = await r.json()
··· 494 ) as r: 495 assert r.status != 200 496 497 + 498 async def test_updateHandle(s, pds_host, auth_headers): 499 async with s.post( 500 pds_host + "/xrpc/com.atproto.identity.updateHandle", 501 headers=auth_headers, 502 + json={"handle": "juliet.test"}, 503 ) as r: 504 assert r.status == 200 505 506 async with s.get( 507 pds_host + "/xrpc/com.atproto.repo.describeRepo", 508 + params={"repo": TEST_DID}, 509 ) as r: 510 assert r.status == 200 511 r = await r.json()