Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds transfers between stores to external attachments #1358
base: main
Are you sure you want to change the base?
Adds transfers between stores to external attachments #1358
Changes from 18 commits
8342a4c
a0cc24c
b62bab8
76db9e5
8b23319
71f1314
a9d36d3
382847d
011e5d1
c53c016
7e34d8f
2addc2f
6de387c
7c0ee63
a44944a
d10ba26
cbaf592
1ffeb45
ca0ee2d
d721c79
7153b44
e1c9a8d
586d5a4
220e6f9
83d984e
fdc3fa4
2dc01d0
34ac344
337c128
4bba04e
5008b27
c553390
981edbb
761d351
4b733b3
5fde08f
30d606e
File filter
Filter by extension
Conversations
Jump to
There are no files selected for viewing
Large diffs are not rendered by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't actually used anywhere. I've left it in for now (since it's already implemented), but we could remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take a look at the endpoint registered above (line 521), it will be matched first and this handler won't be executed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, nicely spotted, I made the mistake of assuming it was done by specificity. Thanks 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about sticking in
locationSummary
for consistency with/transferAll
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am usually not very comfortable with boolean flag params, due to their lack of expressivity.
But am I right to say the
shouldUpdate
may be the default? I only see the flag is kept to false only in tests.In any case, I would suggest for adding more expressivity the following, if that makes sense to you too:
updateOrAttachFile
(where you call the private function withshouldUpdate = true
) andattachFileIfNew
(withshouldUpdate = false
);shouldUpdate: boolean
into{ shouldUpdate = false }: {shouldUpdate: boolean } = {}
or similar;There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting thoughts here! I thought a lot about how to split this - I don't really like the flag either, but ended up in the "tangled implementation" case of that article you linked! 🙂
I'd be happy to do it the way you suggest - make the flagged method private, and expose it as two different public methods. It absolutely feels easier to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I split this up fully, and separated out the implementation in a way that I think it makes it easier to read. 🙂
Let me know if that works for you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment that used to be line 793 gave some reason that is still useful, I think. So I would add some more context:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this is still relevant after the other changes made to this function? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code is acceptable as it is for me, so please skip if you don't agree much about my idea.
I feel like, even it introduces a supplementary SQL query to maintain, we would increase the readability by separating the query to check the existence of the file and do the INSERT/UPDATE. Am I right to say the below code would be equivalent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These look equivalent to me. I'm not sure why it was originally done by catching the unique constraint, but it looks like the only place in the code where that's done.
I'll make this change, and we'll see if @paulfitz or anyone has any opinion :)
Edit: Change is pushed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! If there exist doubts, feel free to rollback
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We hit an error in one of our test cases due to this change, full writeup is below.
Long story short: The way we handle transactions means we can hit a race condition in this example, and catching the "UNIQUE" constraint violation is the simplest way of avoiding that.
I'll change this back, and add a comment explaining why we handle it this way.
Full explanation:
The SQLiteDB class' function
execTransaction
is built to merge all calls into a single transaction, rather than run a transaction per-call (which is how I believed it worked).When several atttachments are being inserted simultaneously in one API call, and because Node's async behaviour is unpredictable, the individual SQL statements are effectively re-ordered at random due to multiple promises running in parallel. This means that even if the SELECT statement says "No records exist", they might be inserted before the "INSERT" actually happens, resulting in a UNIQUE constraint violation.
If we're adding two files at the same time, the resulting order of statements ended up going something like this:
SELECT 1 as fileExists FROM _gristsys_files WHERE ident = ?
SELECT 1 as fileExists FROM _gristsys_files WHERE ident = ?
INSERT INTO main._gristsys_Files (ident, data, storageId) VALUES (?, ?, ?)
INSERT INTO main._gristsys_Files (ident, data, storageId) VALUES (?, ?, ?)
// Unique constraint error thrown here.If they hypothetically had been separate transactions on separate DB connections, the second INSERT would have failed with a "database is locked" error instead - so that's actually no better here.
Hence, catching the UNIQUE constraint error is the cleanest way to avoid this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you think of the following to avoid the boolean flag param:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think splitting them up is a good idea - it definitely makes it more readable.
I think I'd rather duplicate the original function, than pass columns as a string.
That's because there's no risk of bad data accidentally being loaded into the code that formats the result into a typed object (e.g, a missing column).
Additionally, because it lowers the risk of someone doing something silly (such as passing a user-provided column name) in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is done, please let me know if the refactored version is clearer :)