Skip to content

Conversation

@tuhalf
Copy link
Collaborator

@tuhalf tuhalf commented Nov 19, 2025

This pull request introduces a ticket expiration and garbage collection system for P2P objects, primarily focusing on cleaning up stale ticket objects from the database. It modifies the storage and retrieval logic for objects, adds new periodic cleanup jobs, and includes supporting tests to ensure stale tickets are properly expired and removed.

I am not sure about if I delete tickets that's used for payments or not. I need you to check that @dominicletz


Note

Introduce hourly GC that prunes stale ticket objects (tracked via new stored_at column) and drops corresponding Kademlia nodes, plus integrity checks and minor RPC listing tweak.

  • Database / Storage:
    • Add stored_at timestamp column to p2p_objects (with migration) and store it on writes.
    • Implement prune_stale_objects/0 to delete stale ticket rows in chunks and call KademliaLight.drop_nodes/1 for their keys.
  • Kademlia:
    • Add network/0 integrity check to remove nodes missing corresponding objects via drop_nodes/1.
    • New GenServer calls: :reset, :network, and {:drop_nodes, keys}; expose drop_nodes/1 and update reset/0.
  • Cron:
    • Schedule hourly "Ticket GC" job calling Model.KademliaSql.prune_stale_objects/0.
  • RPC:
    • Adjust dio_network to reverse bucket listing before merging with connected nodes.
  • Tests:
    • Add tests ensuring GC keeps recent tickets and removes stale ones.

Written by Cursor Bugbot for commit 8c9a8f7. This will update automatically on new commits. Configure here.

@tuhalf tuhalf requested a review from dominicletz November 19, 2025 07:41
@gemini-code-assist
Copy link

Summary of Changes

Hello @tuhalf, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request establishes a robust system for managing the lifecycle of P2P ticket objects within the database. By introducing automatic garbage collection and defining clear criteria for ticket staleness, it ensures that the system remains efficient and free of outdated data, improving overall database health and performance. The changes encompass schema modifications, new background jobs, and integrated staleness checks during object retrieval.

Highlights

  • Automated Ticket Garbage Collection: Introduced a new hourly cron job named 'Ticket GC' that automatically prunes stale P2P ticket objects from the database, preventing the accumulation of expired data.
  • Staleness Criteria for Tickets: Implemented logic to determine if a ticket is 'stale' based on two criteria: a time-to-live (TTL) of 24 hours since storage, and an epoch-based check to ensure tickets are not significantly older than the current blockchain epoch.
  • Database Schema and Object Handling Updates: Modified the p2p_objects table to include a stored_at timestamp. Object storage (put_object) now records this timestamp, and object retrieval functions (object, scan, objects) now automatically filter out and delete stale objects upon access.
  • Comprehensive Testing: Added new unit tests to verify the correct behavior of stale ticket expiration based on TTL and the functionality of the garbage collection process.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a valuable ticket expiration and garbage collection system for P2P objects, which will help maintain the database by cleaning up stale ticket objects. The implementation includes a database migration for a stored_at timestamp, a new cron job for periodic cleanup, and logic to filter stale objects on read. My review focuses on potential performance improvements and enhancing code clarity. I've identified that the prune_stale_objects function could lead to high memory usage with a large database and have suggested a more scalable batching approach. Additionally, I've provided suggestions to make some parts of the code more idiomatic and concise. Regarding your question about deleting tickets used for payments, the current logic correctly implements a cleanup strategy for a distributed cache. It assumes that tickets that are old (by time or epoch) are no longer relevant, which is a reasonable approach as submitted tickets are recorded on-chain.

@tuhalf
Copy link
Collaborator Author

tuhalf commented Nov 25, 2025

Other than making pruning an sql command, fixed a bug on missing Kademlia objects. Handled missing Kademlia objects to stop decode! crashes: KBuckets.object now returns nil when absent, connection/RPC helpers skip missing objects, and edge/RPC responses filter out nil objects.

Copy link
Member

@dominicletz dominicletz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as per above we need to keep the network state consistent

["getnodes", node] ->
KademliaLight.find_nodes(node)
|> Enum.map(&KBuckets.object/1)
|> Enum.filter(& &1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nil filtering should not be needed

node: Json.prepare!(KBuckets.object(item), big_x: false),
retries: encode16(item.retries)
}
case KBuckets.object(item) do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if at all we should do nil checking insideKademliaLight.network() and if the in-memory table has any entries that are not in the db anymore we need to throw a fatal error and rebuild/clear the in-memory table

lib/kbuckets.ex Outdated
case Model.KademliaSql.object(key(item)) do
nil -> nil
binary -> Object.decode!(binary)
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as below comment on KademliaLight.network() consistency -- if the db is not consistent this should crash and probably flush the memory table

host = Server.host(server)
port = Server.peer_port(server)
Network.Server.ensure_node_connection(PeerHandlerV2, node_id, host, port)
case KBuckets.object(item) do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

error ->
Logger.warning(
"Failed to get a result from #{Wallet.printable(node_id)} #{inspect(error)}"
case ensure_node_connection(node) do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as below, shouldn't be happening

@tuhalf
Copy link
Collaborator Author

tuhalf commented Nov 26, 2025

I commited everything but nil check. I will complete it today

@tuhalf tuhalf requested a review from dominicletz November 26, 2025 19:01

def network() do
call(fn _from, state -> {:reply, state.network, state} end)
call(&ensure_network_integrity/2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

network =     call(fn _from, state -> {:reply, state.network, state} end)
if ensure_network_integrity(network) == false do
   GenServer.cast(KademliaLight, :integrity_failed)
   raise :network_integrity_failed
end
network

@dominicletz dominicletz merged commit 440afb2 into main Nov 27, 2025
3 checks passed
@dominicletz dominicletz deleted the fix_kademlia_for_old_tickets branch November 27, 2025 11:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants