core icon indicating copy to clipboard operation
core copied to clipboard

Chat assistant content for installing on Linux is wrong

Open richlander opened this issue 5 months ago • 0 comments

We need to work on this by having better release notes / docs.

Prompt: install .NET on Ubuntu 24.04

Wrong Answers

GitHub Copilot -- GPT 4.1

Image

GitHub Copilot -- o3

Image

ChatGPT 4o:

Image

DeepSeek v3

Image

Good Answers

ChatGPT o3:

Image

Claude Sonnet 4:

Image

Claude Opus 4:

Image

Gemini Flash 2.5:

Image

Gemini 2.5 Pro:

Image

Very bad answers

Image

richlander avatar Jul 12 '25 01:07 richlander

Codecov Report

:x: Patch coverage is 66.26506% with 28 lines in your changes missing coverage. Please review. :white_check_mark: Project coverage is 54.70%. Comparing base (69d72c2) to head (a3369ab).

Files with missing lines Patch % Lines
...ucture.Dapper/Repositories/CollectionRepository.cs 0.00% 21 Missing :warning:
...tityFramework/Repositories/CollectionRepository.cs 83.33% 7 Missing :warning:
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6677      +/-   ##
==========================================
- Coverage   58.55%   54.70%   -3.85%     
==========================================
  Files        1925     1926       +1     
  Lines       85669    85721      +52     
  Branches     7669     7670       +1     
==========================================
- Hits        50162    46897    -3265     
- Misses      33649    37040    +3391     
+ Partials     1858     1784      -74     

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

:rocket: New features to boost your workflow:
  • :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

codecov[bot] avatar Dec 03 '25 01:12 codecov[bot]

Logo Checkmarx One – Scan Summary & Details09f57f79-f3c1-426c-9c05-8ab582dbb80f

Great job! No new security vulnerabilities introduced in this pull request

github-actions[bot] avatar Dec 03 '25 01:12 github-actions[bot]

@bitwarden/dept-dbops Can you please review my approach to locks/concurrency here and let me know if I'm on the right path? See the PR description for more info. My current approach has my tests working, but has broken every other instance where we're manually creating an EF transaction. I can fix those by explicitly using a strategy, but better to get some feedback before I spend more time on it.

eliykat avatar Dec 03 '25 04:12 eliykat

Claude finished @eliykat's task —— View job


Code Review Complete

I've completed a comprehensive review of this PR. The core concurrency approach is sound, but there are critical issues that need to be addressed before merging.


Executive Summary

The idempotent stored procedure with SERIALIZABLE isolation is the right approach for preventing duplicate "My Items" collections. However, the implementation has:

  • 2 critical bugs that will cause runtime failures
  • 1 critical architectural concern about global EF configuration changes
  • Several important improvements needed for robustness

Critical Issues (Must Fix)

1. Missing @CollectionId Parameter in Dapper Implementation ❌

File: src/Infrastructure.Dapper/Repositories/CollectionRepository.cs:370

The Dapper implementation generates a collectionId but never passes it to the stored procedure:

var collectionId = CoreHelpers.GenerateComb();  // Generated but NOT used!
var parameters = new DynamicParameters();
// Missing: parameters.Add("@CollectionId", collectionId);

The stored procedure expects @CollectionId as a required parameter, so this will cause INSERT failures.

Fix:

parameters.Add("@CollectionId", collectionId);
parameters.Add("@OrganizationId", organizationId);
// ... rest of parameters

2. EnableRetryOnFailure Breaking Change ❌

File: src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs:46-79

The Problem:

You've added EnableRetryOnFailure() to ALL database configurations (SQL Server, PostgreSQL, MySQL). This is a global change that fundamentally breaks existing code patterns.

When EnableRetryOnFailure is enabled, EF does not allow manual transaction creation with BeginTransactionAsync() unless you use an execution strategy. This throws:

InvalidOperationException: The configured execution strategy 'SqlServerRetryingExecutionStrategy' 
does not support user initiated transactions.

Evidence this breaks existing code:

Several files in the codebase use BeginTransactionAsync() without execution strategies:

  • Infrastructure.EntityFramework/Repositories/OrganizationRepository.cs (multiple places)
  • Infrastructure.EntityFramework/Repositories/CipherRepository.cs
  • Infrastructure.Dapper/Repositories/CollectionRepository.cs:340 (your own code!)

Your concern was valid! You mentioned this "broke every other instance where we're manually creating an EF transaction" - that's exactly what's happening.

Two Solutions:

Option A (Recommended): Remove global EnableRetryOnFailure and only use execution strategy where needed:

// In EntityFrameworkServiceCollectionExtensions.cs - REMOVE EnableRetryOnFailure
options.UseSqlServer(connectionString, b =>
{
    // b.EnableRetryOnFailure();  // REMOVE THIS
});

// Keep your execution strategy approach in CollectionRepository.UpsertDefaultCollectionAsync
// This is the only place that needs retry logic for this feature

Option B: Audit and fix ALL existing BeginTransactionAsync calls (likely 10+ locations) to use execution strategies:

var strategy = dbContext.Database.CreateExecutionStrategy();
return await strategy.ExecuteAsync(async () =>
{
    using var transaction = await dbContext.Database.BeginTransactionAsync();
    // ... transaction work
});

My Recommendation: Go with Option A. The global retry strategy is overkill for this specific use case and creates unnecessary breaking changes throughout the codebase.


Important Issues (Should Fix)

3. DbContext Lifecycle Issue in EF Implementation ⚠️

File: src/Infrastructure.EntityFramework/Repositories/CollectionRepository.cs:824-899

The dbContext is created outside the ExecuteAsync block:

using var scope = ServiceScopeFactory.CreateScope();
var dbContext = GetDatabaseContext(scope);  // Outside ExecuteAsync
var strategy = dbContext.Database.CreateExecutionStrategy();

return await strategy.ExecuteAsync(async () =>
{
    // Uses same dbContext on retry - could be in corrupted state

If EF retries after a deadlock, it reuses the same DbContext instance, which may be in a corrupted state.

Fix: Move scope and context creation inside ExecuteAsync:

public async Task<bool> UpsertDefaultCollectionAsync(Guid organizationId, Guid organizationUserId, string defaultCollectionName)
{
    // Get execution strategy first
    using var tempScope = ServiceScopeFactory.CreateScope();
    var strategy = GetDatabaseContext(tempScope).Database.CreateExecutionStrategy();

    return await strategy.ExecuteAsync(async () =>
    {
        // Fresh scope and context for each execution (including retries)
        using var scope = ServiceScopeFactory.CreateScope();
        var dbContext = GetDatabaseContext(scope);
        
        using var transaction = await dbContext.Database.BeginTransactionAsync(System.Data.IsolationLevel.Serializable);
        // ... rest of implementation
    });
}

4. Test Coverage Gaps 📋

File: test/Infrastructure.IntegrationTest/AdminConsole/Repositories/CollectionRepository/UpsertDefaultCollectionTests.cs

Your concurrent test (lines 82-109) is excellent ✓, but you're missing:

  1. Different users in same organization - verify isolation between users
  2. Same user in different organizations - verify separate "My Items" per org
  3. Permission verification - The test that verified Manage=true, ReadOnly=false, HidePasswords=false appears to have been present in earlier iterations but was removed
  4. Negative tests - invalid IDs, null collection name

Recommended additions:

[Theory, DatabaseData]
public async Task UpsertDefaultCollectionAsync_ShouldCreateSeparateCollections_ForDifferentUsersInSameOrganization(
    IOrganizationRepository organizationRepository,
    IUserRepository userRepository,
    IOrganizationUserRepository organizationUserRepository,
    ICollectionRepository collectionRepository)
{
    var organization = await organizationRepository.CreateTestOrganizationAsync();
    var user1 = await userRepository.CreateTestUserAsync();
    var user2 = await userRepository.CreateTestUserAsync();
    var orgUser1 = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user1);
    var orgUser2 = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user2);
    
    // Both should create successfully
    var created1 = await collectionRepository.UpsertDefaultCollectionAsync(organization.Id, orgUser1.Id, "My Items");
    var created2 = await collectionRepository.UpsertDefaultCollectionAsync(organization.Id, orgUser2.Id, "My Items");
    
    Assert.True(created1);
    Assert.True(created2);
    
    // Verify each user has their own collection
    var user1Collections = await collectionRepository.GetManyByUserIdAsync(user1.Id);
    var user2Collections = await collectionRepository.GetManyByUserIdAsync(user2.Id);
    
    Assert.Single(user1Collections.Where(c => c.Type == CollectionType.DefaultUserCollection));
    Assert.Single(user2Collections.Where(c => c.Type == CollectionType.DefaultUserCollection));
}

Concurrency Approach Review ✓

Your specific question: "Can you please review my approach to locks/concurrency here?"

Stored Procedure Approach ✓ Correct

The SERIALIZABLE isolation level in the stored procedure is correct for preventing TOCTOU race conditions:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;  -- ✓ GOOD
BEGIN TRANSACTION;

SELECT @ExistingCollectionId = c.Id
FROM [dbo].[Collection] c
INNER JOIN [dbo].[CollectionUser] cu ON cu.CollectionId = c.Id
WHERE cu.OrganizationUserId = @OrganizationUserId
    AND c.OrganizationId = @OrganizationId
    AND c.Type = 1;

This acquires range locks that prevent:

  • Concurrent INSERTs that would match the WHERE clause (phantom reads)
  • Race conditions between check-and-insert
  • Duplicate collections being created

Deadlock Risk Mitigation ✓ Appropriate

Yes, SERIALIZABLE increases deadlock risk. Your mitigation strategy (EF execution strategy with automatic retry) is appropriate for SQL Server error 1205 (deadlock victim).

However: The stored procedure manages its own transaction, which is fine for standalone calls but could cause issues if ever called within an existing transaction context. Document this limitation.

EF Implementation Pattern ✓ Mostly Correct

var strategy = dbContext.Database.CreateExecutionStrategy();
return await strategy.ExecuteAsync(async () =>
{
    using var transaction = await dbContext.Database.BeginTransactionAsync(System.Data.IsolationLevel.Serializable);
    // ...
});

This pattern is correct! The transaction is created inside the execution strategy, allowing EF to retry the entire transaction on deadlock.

Minor refinement needed: Move the DbContext creation inside ExecuteAsync as well (see issue #3 above).


Minor Issues

5. Input Validation Missing

Add defensive validation in both implementations:

public async Task<bool> UpsertDefaultCollectionAsync(Guid organizationId, Guid organizationUserId, string defaultCollectionName)
{
    if (organizationId == Guid.Empty) throw new ArgumentException(nameof(organizationId));
    if (organizationUserId == Guid.Empty) throw new ArgumentException(nameof(organizationUserId));
    if (string.IsNullOrWhiteSpace(defaultCollectionName)) throw new ArgumentException(nameof(defaultCollectionName));
    // ...
}

6. Hard-Coded Enum Value in SQL

File: src/Sql/dbo/Stored Procedures/Collection_UpsertDefaultCollection.sql:30

AND c.Type = 1;  -- Already has comment at top, but add inline too

Consider adding inline comment:

AND c.Type = 1  -- CollectionType.DefaultUserCollection

Security Review ✓ Pass

Zero-Knowledge Principle: No violations - collection names are encrypted client-side
SQL Injection: All parameters properly parameterized
Logging: No sensitive data logged
Transaction Integrity: Proper rollback on errors


Recommendations Summary

Must Fix Before Merge:

  1. Add missing @CollectionId parameter in Dapper implementation
  2. Resolve the EnableRetryOnFailure global configuration issue (recommend Option A: remove it globally)

Should Fix:

  1. Move DbContext creation inside ExecuteAsync
  2. Add test coverage for user isolation and multi-org scenarios
  3. Restore permission verification tests

Nice to Have:

  1. Add input validation
  2. Add inline comment for enum value in SQL

Overall Assessment

Core Design: ✅ Sound approach
Concurrency Strategy: ✅ Correct use of SERIALIZABLE isolation
Implementation Quality: ⚠️ Has critical bugs that need fixing
Test Coverage: ⚠️ Good for concurrency, gaps in other scenarios

The fundamental architecture is solid, but the implementation needs refinement before merging. The global EF configuration change is the biggest concern as it has ripple effects throughout the codebase.

claude[bot] avatar Dec 03 '25 14:12 claude[bot]

@claude I disagree with "1. Missing @CollectionId Parameter in Dapper Implementation" - double check that you've read this correctly.

eliykat avatar Dec 04 '25 01:12 eliykat

Claude finished @eliykat's task —— View job


Re-verification Complete

You are absolutely correct - I apologize for the error in my review.

Corrected Finding

Issue #1 - Missing @CollectionId Parameter: ❌ INCORRECT

I was wrong about this. Looking at src/Infrastructure.Dapper/Repositories/CollectionRepository.cs:373, the code does properly add the @CollectionId parameter:

var collectionId = CoreHelpers.GenerateComb();
var now = DateTime.UtcNow;
var parameters = new DynamicParameters();
parameters.Add("@CollectionId", collectionId);  // ✓ THIS IS PRESENT
parameters.Add("@OrganizationId", organizationId);
parameters.Add("@OrganizationUserId", organizationUserId);
// ... rest of parameters

I must have misread the code during my initial review. This implementation is correct.


Updated Critical Issues (Must Fix)

With issue #1 removed, there is now only one critical issue:

1. EnableRetryOnFailure Breaking Change ❌

File: src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs:46-79

You've added EnableRetryOnFailure() to ALL database configurations globally. This breaks existing code that uses BeginTransactionAsync() without execution strategies.

The Problem:

When EnableRetryOnFailure is enabled, EF throws:

InvalidOperationException: The configured execution strategy 'SqlServerRetryingExecutionStrategy' 
does not support user initiated transactions.

Evidence from your own comment: You mentioned this "broke every other instance where we're manually creating an EF transaction" - that's exactly what happens.

Recommendation: Remove global EnableRetryOnFailure and only use execution strategy where needed (your UpsertDefaultCollectionAsync implementation):

// In EntityFrameworkServiceCollectionExtensions.cs - REMOVE EnableRetryOnFailure
options.UseSqlServer(connectionString, b =>
{
    // b.EnableRetryOnFailure();  // REMOVE THIS
});

// Your execution strategy approach in CollectionRepository.UpsertDefaultCollectionAsync is correct
// Keep it as-is - it's the only place that needs retry logic for this feature

Remaining Review Items

All other items from my original review remain valid:

Important Issues (Should Fix):

  • Issue #2: DbContext lifecycle in EF implementation should move scope creation inside ExecuteAsync
  • Issue #3: Test coverage gaps for user isolation and multi-org scenarios

Minor Issues:

  • Issue #4: Input validation missing
  • Issue #5: Consider inline comment for hard-coded enum value in SQL

Positive Findings:

  • ✅ Concurrency approach with SERIALIZABLE isolation is correct
  • ✅ Stored procedure design is sound
  • ✅ Security review passes
  • ✅ Deadlock mitigation strategy is appropriate

Apology

I apologize for the incorrect finding about the missing parameter. Thank you for pushing back and asking me to double-check. The Dapper implementation is correct on this point.

The main remaining concern is the global EnableRetryOnFailure configuration change and its breaking impact on existing transaction code throughout the codebase.


View job

claude[bot] avatar Dec 04 '25 01:12 claude[bot]