Skip to content

fix: stop swallowing multi-row DB errors and dedupe profile pictures - #810

Open
p3xz wants to merge 1 commit into
Women-Coding-Community:mainfrom
p3xz:fix/747-profile-picture-dedup
Open

p3xz wants to merge 1 commit into
Women-Coding-Community:mainfrom
p3xz:fix/747-profile-picture-dedup

Conversation

@p3xz

@p3xz p3xz commented Oct 5, 2026

Copy link
Copy Markdown

Fixes #747

Bug: PostgresMemberProfilePictureRepository.findByMemberId / findById caught every DataAccessException and returned Optional.empty(). Two concurrent profile-picture uploads could race between the read and the insert, leaving two member_profile_picture rows for one member. Every later read then threw IncorrectResultSizeDataAccessException, which was swallowed to empty, so the member's picture silently and permanently vanished from all responses (and the cleanup path in ResourceService.uploadProfile never fired).

Fix (3 parts):

  1. findByMemberId/findById now catch only EmptyResultDataAccessException (genuinely no row). Real DB errors, including duplicate-row reads, propagate instead of being masked as "no picture".
  2. create() is now an upsert: INSERT ... ON CONFLICT (member_id) DO UPDATE SET resource_id = EXCLUDED.resource_id, so a concurrent re-upload replaces the row instead of duplicating it.
  3. New Flyway migration V40 adds a UNIQUE constraint on member_id as the schema-level guarantee.

Verification:

  • Compiled the affected classes with javac (Gradle daemon can't run in this sandbox); PMD (repo's custom-ruleset.xml / custom-ruleset-test.xml) reports no violations on the changed files.
  • PostgresMemberProfilePictureRepositoryTest: 10/10 pass. Before/after check: the 3 new tests (duplicate-row error propagates for both reads; upsert SQL contains ON CONFLICT (member_id)) fail against the old code (3 failed, 7 passed) and pass with this fix.

…omen-Coding-Community#747)

findByMemberId/findById caught every DataAccessException and returned
Optional.empty(). Two concurrent uploads could therefore leave two
member_profile_picture rows; every later read threw
IncorrectResultSizeDataAccessException, which was swallowed, so the
member's picture silently and permanently vanished from all responses.

- Narrow the catch to EmptyResultDataAccessException only, so genuine
  DB errors (duplicate rows, connection failures) propagate instead of
  being masked as 'no picture'.
- create() now upserts on member_id (ON CONFLICT DO UPDATE), and a new
  Flyway migration adds a UNIQUE constraint on member_id, so concurrent
  uploads can no longer leave duplicate rows behind.
- Tests: duplicate-row reads now assert the error propagates instead of
  returning empty; create() asserts the upsert SQL.
@sonarqubecloud

sonarqubecloud Bot commented Oct 5, 2026

Copy link
Copy Markdown

@womencodingcommunity womencodingcommunity left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review

return Optional.ofNullable(profilePicture);
} catch (DataAccessException e) {
} catch (EmptyResultDataAccessException e) {
// No row for this resource id: genuinely absent, not a swallowed DB error.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this inline comment should be part of the javadoc.

return Optional.ofNullable(profilePicture);
} catch (DataAccessException e) {
} catch (EmptyResultDataAccessException e) {
// No row for this member: genuinely absent, not a swallowed DB error.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

same as previous comment, please move to javadoc instead of inline comment.

@dricazenck dricazenck left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the contribution, please consider the small comment to move info to javadoc instead of inline comment. This keeps the code cleaner and easier to maintain and read.

This branch has not been deployed

No deployments
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.

bug: Concurrent profile-picture uploads create duplicate rows; picture then silently vanishes

3 participants