Add completed_event_id FK to prevent session replay#1158
Add completed_event_id FK to prevent session replay#1158Mshehu5 wants to merge 1 commit intopayjoin:masterfrom
Conversation
Pull Request Test Coverage Report for Build 19371495926Details
💛 - Coveralls |
|
Thank you for taking on this @Mshehu5 . on first pass , this works as intended |
spacebear21
left a comment
There was a problem hiding this comment.
The application should just need to properly implement the close() method to set the column appropriately, as close() is called by the payjoin state machine when appropriate; the application should never call save_event() directly. This makes me realize the documentation around the SessionPersister methods needs to be clearer.
c0fd47a to
de46c09
Compare
arminsabouri
left a comment
There was a problem hiding this comment.
Not a huge fan of using string matching to find the "closed" event and left some other suggestions.
fbe10e6 to
4866994
Compare
4866994 to
b7c8741
Compare
arminsabouri
left a comment
There was a problem hiding this comment.
Its not clear to me what problem the changes in payjoin/src/core/send/v2 and its corresponding change in payjoin-cli are solving. They seem unrelated to the original ticket and should be moved to a different PR once its clear what the problem actually is.
payjoin-cli/src/db/v2.rs
Outdated
| "UPDATE send_sessions SET completed_at = ?1 WHERE session_id = ?2", | ||
| params![now(), *self.session_id], | ||
| "UPDATE send_sessions SET completed_at = ?1, completed_event_id = ?2 WHERE session_id = ?3", | ||
| params![now(), completed_event_id, *self.session_id], |
There was a problem hiding this comment.
What happens is completed_event_id is None? There should will always be a session event before close is called. Should we make this expectation explicit?
understood, seems the reason I made the change in |
dad306d to
f02b04b
Compare
f02b04b to
622a6c6
Compare
arminsabouri
left a comment
There was a problem hiding this comment.
Just a few more things. Mainly noticing some unrelated changes
| let conn = self.db.get_connection()?; | ||
| let mut stmt = conn.prepare( | ||
| "SELECT event_data FROM send_session_events WHERE session_id = ?1 ORDER BY created_at ASC", | ||
| "SELECT event_data FROM send_session_events WHERE session_id = ?1 ORDER BY id ASC", |
There was a problem hiding this comment.
Since the ID is always auto-incremeneting, i think this would work. But why make this change? It seems unrelated to original intent of the PR. AFAICT created_at should still be a field on send_session_events
There was a problem hiding this comment.
The reason for this change is I noticed events saved within the same second share the same created_at (seconds precision), making created_at non-deterministic and causing out-of-order result
I aslo wanted to add DB transaction to make it more atomic but I think it will be better for a followup PR as it is out of scope
There was a problem hiding this comment.
Thanks for the explanation. In the future lets break this kind of change into its own commit please or a different PR. It would help speed up the review process.
There was a problem hiding this comment.
Do we think that this is worth making its own issue?
There was a problem hiding this comment.
yes! thanks for catching that
622a6c6 to
37aa822
Compare
payjoin-cli/src/db/v2.rs
Outdated
| if matches!(event, SenderSessionEvent::Closed(_)) { | ||
| conn.execute( | ||
| "UPDATE send_sessions SET completed_at = ?1, completed_event_id = ?2 WHERE session_id = ?3", | ||
| params![now(), event_id, *self.session_id], |
There was a problem hiding this comment.
The time here would be inacurrate for when the actual session event was created . No need to call now() again here . Rather use the time when the actual session_event was created as this just tells us the time this DB entry was made
There was a problem hiding this comment.
Thanks for the review Zealsham!
It seems your comments are based on an older version of this PR. I’d really appreciate it if you could take a look at the latest changes as well.
| let conn = self.db.get_connection()?; | ||
| let mut stmt = conn.prepare( | ||
| "SELECT event_data FROM send_session_events WHERE session_id = ?1 ORDER BY created_at ASC", | ||
| "SELECT event_data FROM send_session_events WHERE session_id = ?1 ORDER BY id ASC", |
There was a problem hiding this comment.
Thanks for the explanation. In the future lets break this kind of change into its own commit please or a different PR. It would help speed up the review process.
spacebear21
left a comment
There was a problem hiding this comment.
cACK, but I think the query for completed_event_id in the close() methods needs to be more specific than just "the last event". Otherwise we risk ending up out of sync if e.g. close() is called prematurely, then completed_event_id would point to a non-Closed event.
payjoin-cli/src/db/v2.rs
Outdated
| let completed_event_id: Option<i64> = conn | ||
| .query_row( | ||
| "SELECT id FROM receive_session_events | ||
| WHERE session_id = ?1 | ||
| ORDER BY id DESC LIMIT 1", | ||
| params![*self.session_id], | ||
| |row| row.get(0), | ||
| ) | ||
| .ok(); |
There was a problem hiding this comment.
AFAICT this is just querying for the "last event" ID, not a completed event specifically? I think it needs to explicitly filter for "Closed" session events and error if one is not found.
37aa822 to
735d4f5
Compare
Add completed_event_id foreign key columns to both send_sessions and receive_sessions tables that reference the exact event that closed the session to replace completed_at column.
735d4f5 to
6dccbd4
Compare
| let completed_event_id: i64 = match conn.query_row( | ||
| "SELECT id FROM send_session_events | ||
| WHERE session_id = ?1 | ||
| AND json_extract(event_data, '$.Closed') IS NOT NULL |
There was a problem hiding this comment.
Interesting, I wasn't familiar with the json_extract syntax. Is there a reason we couldn't do simple string matching? e.g.
| AND json_extract(event_data, '$.Closed') IS NOT NULL | |
| AND event_data = 'Closed' |
or
| AND json_extract(event_data, '$.Closed') IS NOT NULL | |
| AND event_data LIKE '%Closed' |
There was a problem hiding this comment.
I think using a string patter like "Closed" will be easily subject to bugs. And it prevents us from using the string "Closed" in a future session events for a not so obvious reason. Can we assume the last event will always be a closed event if close is being called? If so I would just order all the session events and limit to 1 to take the last event.
This comment is from Armin that’s why I didn’t use string matching. I had originally done it that way, but changed it based on his feedback.
There was a problem hiding this comment.
I see, apologies I had missed that earlier piece of review. @arminsabouri my concern was that if we simply query the last event, then we run the risk of closing a session that never recorded a Closed event and erroneously marking it as closed (the user can call close arbitrarily).
I agree string matching is not very robust, but fwiw json_extract still uses string matching.
There was a problem hiding this comment.
Based on my research json_extract(event_data, '$.Closed') isn’t just string matching. the SQLite query parses the JSON and only returns a value if there’s a top-level "Closed" key, so it won’t match strings like "closed", "ClosedEvent", or "NotClosed" that may appear elsewhere in the payload unlike %Closed% which just matches anything containing the substring "Closed"
I also pushed a change which handles matching Closed event in Rust code see https://github.com/payjoin/rust-payjoin/compare/735d4f5c5b75a466754283e6f1f646650df421b6..6dccbd4441c59dfda9a48263c0462a891b489108
If this approach or any alternative is more favorable I can make those changes
|
@Mshehu5 Thank you for spending time to explore this problem space and present great solutions! However, after looking at the current state of this PR (and alternatives paths that you have dillegently proposed) I don't think the added complexity is worth it. Specifically, the string-matching approach doesn’t set a good precedent for applications that will model their behavior after pj-cli. The best path forward is to close the ticket as won't be fixed. |
This PR adds completed_event_id foreign key columns to both send_sessions and receive_sessions tables. This column references the exact event row that closed the session.
Closes #1146
Please confirm the following before requesting review:
AI
in the body of this PR.