A social pastebin built on atproto.
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

add cache for bites

+54 -24
+54 -24
main.py
··· 69 69 SLINGSHOT_URL = "https://slingshot.microcosm.blue" 70 70 71 71 _avatar_cache: dict[str, tuple[bytes, str, float]] = {} 72 - AVATAR_TTL = 1800 # 30mins 72 + AVATAR_TTL = 3600 73 + 74 + _bite_cache: dict[str, tuple[Any, float]] = {} 75 + BITE_TTL = 3600 73 76 74 77 # ============================================================================= 75 78 # Database ··· 580 583 def index() -> str: 581 584 recent = fetch_recent_bites(limit=5) 582 585 586 + # Pre-populate bite cache from feed data 587 + now = time.time() 588 + for bite in recent: 589 + key = f"{bite.get('did')}/{bite.get('rkey')}" 590 + if key not in _bite_cache: 591 + _bite_cache[key] = ({ 592 + "title": bite.get("title", "Untitled"), 593 + "content": bite.get("content", ""), 594 + "created_at": bite.get("created_at", ""), 595 + "cid": "", 596 + }, now) 597 + 583 598 if g.user: 584 599 return render_template( 585 600 "create.html", recent=recent, did=g.user["did"], handle=g.user["handle"] ··· 644 659 return result 645 660 did, handle, pds_url, profile = result 646 661 647 - try: 648 - client = Client(SLINGSHOT_URL) 649 - response = client.com.atproto.repo.get_record( 650 - models.ComAtprotoRepoGetRecord.Params( 651 - repo=did, 652 - collection=COLLECTION, 653 - rkey=rkey, 662 + cache_key = f"{did}/{rkey}" 663 + now = time.time() 664 + bite = None 665 + if cache_key in _bite_cache: 666 + cached, ts = _bite_cache[cache_key] 667 + if now - ts < BITE_TTL: 668 + bite = cached 669 + 670 + if bite is None: 671 + try: 672 + client = Client(SLINGSHOT_URL) 673 + response = client.com.atproto.repo.get_record( 674 + models.ComAtprotoRepoGetRecord.Params( 675 + repo=did, 676 + collection=COLLECTION, 677 + rkey=rkey, 678 + ) 654 679 ) 655 - ) 656 - except BadRequestError: 657 - abort(404, "Bite not found.") 658 - except NetworkError: 659 - abort(502, "Could not reach this user's server.") 660 - except Exception: 661 - abort(500, "Something went wrong loading this bite.") 680 + bite = { 681 + "title": response.value["title"] or "Untitled", 682 + "content": response.value["content"] or "", 683 + "created_at": response.value["createdAt"] or "", 684 + "cid": response.cid, 685 + } 686 + _bite_cache[cache_key] = (bite, now) 687 + except BadRequestError: 688 + abort(404, "Bite not found.") 689 + except NetworkError: 690 + abort(502, "Could not reach this user's server.") 691 + except (KeyError, TypeError): 692 + abort(500, "This bite has missing or malformed data.") 693 + except Exception: 694 + abort(500, "Something went wrong loading this bite.") 695 + 696 + title = bite["title"] 697 + content = bite["content"] 698 + created_at = bite["created_at"] 662 699 663 700 raw_replies = fetch_replies(did, rkey) 664 701 replies = hydrate_replies(raw_replies) ··· 673 710 if not already_indexed: 674 711 replies.insert(0, pending) 675 712 676 - try: 677 - title: str = response.value["title"] or "Untitled" 678 - content: str = response.value["content"] or "" 679 - created_at: str = response.value["createdAt"] or "" 680 - except KeyError, TypeError: 681 - abort(500, "This bite has missing or malformed data.") 682 - return "" # unreachable, satisfies type checker 683 - 684 713 return render_template( 685 714 "view.html", 686 715 title=title, ··· 692 721 profile=profile, 693 722 created_at=created_at, 694 723 at_uri=f"at://{did}/{COLLECTION}/{rkey}", 695 - cid=response.cid, 724 + cid=bite.get("cid", ""), 696 725 replies=replies, 697 726 ) 698 727 ··· 756 785 if isinstance(resp, WerkzeugResponse): 757 786 return resp 758 787 788 + _bite_cache.pop(f"{g.user['did']}/{rkey}", None) 759 789 flash("Bite deleted.") 760 790 return redirect(url_for("list_bites", identifier=g.user["did"])) 761 791