Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

SQL Server: every stream lookup by name does a key lookup to get `Version`

Đang mở
#617 1 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 7 ngày

@nmummau đang làm issue này rồi.

Từ ngày 14/10/2026.

  • #621 của @nmummau — đang mở

Đánh giá

Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức phù hợp với người mới
25/100
Loại issue
Tái cấu trúc
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Đình trệ
Công nghệ
csharp, sql
Lĩnh vực
backend, databases

Hướng nghiên cứu

Start with the schema in src/SqlServer/src/Eventuous.SqlServer/Scripts/1_Schema.sql, where the Streams table and UQ_StreamName are defined, and with check_stream, append_events and the CATCH block that matches the constraint name. Pull request #621 is already open against this issue, so check it before starting. Done means the lookup by name becomes a covered index seek, existing databases get an upgrade step, and the concurrency tests from #598 still pass.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Summary

check_stream, read_stream_forwards, and read_stream_backwards all start with the same query:

SELECT
    @current_version = [Version],
    @stream_id = StreamId
FROM __schema__.Streams
WHERE StreamName = @stream_name;

The only index on StreamName is the unique constraint UQ_StreamName. It holds StreamName and the clustered key StreamId, but not Version. So SQL Server seeks UQ_StreamName and then does a key lookup into PK_Streams to get Version. This occurs once for every append and every stream read.

How I found it

We capture the actual execution plans from our integration tests and analyze them with Performance Studio. It reports this statement as a critical "Key Lookup":

Key Lookup — SQL Server found rows via a nonclustered index but had to go back to the clustered index for additional columns. Columns fetched: Streams.Version

Statement in the plan (from check_stream, called by append_events):

SELECT @current_version = [Version], @stream_id = StreamId
FROM eventuous.Streams
WHERE StreamName = N'InventoryNodeStock-102'

Environment: Eventuous 0.17.0, SQL Server 2022. The schema in src/SqlServer/src/Eventuous.SqlServer/Scripts/1_Schema.sql on dev has the same index, so the plan shape is the same there.

Suggested change

Replace the unique constraint with a unique index that has the same name and includes Version:

CREATE TABLE __schema__.Streams
(
    StreamId   INT IDENTITY (1,1) NOT NULL,
    StreamName NVARCHAR(850)      NOT NULL,
    [Version]  INT DEFAULT (-1)   NOT NULL,
    CONSTRAINT PK_Streams PRIMARY KEY CLUSTERED (StreamId) WITH (OPTIMIZE_FOR_SEQUENTIAL_KEY = ON),
    CONSTRAINT CK_VersionGteNegativeOne CHECK ([Version] >= -1),
    INDEX UQ_StreamName UNIQUE NONCLUSTERED (StreamName) INCLUDE ([Version])
);

A UNIQUE constraint cannot have INCLUDE columns, so it must become a unique index. Keep the name UQ_StreamName: the CATCH block in check_stream looks for that name in the error message, and already handles both error 2627 (constraint) and 2601 (unique index).

Trade-offs

  • Benefit: The lookup by name becomes one index seek. This saves a few logical reads on each append and each stream read, and the read path touches one index instead of two.
  • Cost: The UPDATE ... SET [Version] in append_events must now also write the nonclustered index. Version is a 4-byte INT, so the index stays narrow, but it is one more index write for each append.
  • Locking: check_stream takes UPDLOCK on the row it reads. With a covering index, that read locks only the UQ_StreamName key, not the clustered row. Two concurrent appends to the same stream still meet on the same UQ_StreamName key, so they must still be serialized, but the concurrency tests added in #598 should confirm this.
  • Existing databases: 1_Schema.sql creates the table only IF OBJECT_ID(...) IS NULL, so existing databases do not get the change. The change needs an upgrade step (drop the constraint, create the index), or a note in the release notes.

The gain for each call is small. But this query runs on the hot path of every append and every aggregate load, so I think the change is worth it. I can open a PR if you agree with the approach.

Ngôn ngữ chính
C#
Star
512
Fork
99
Merge trung bình
5 ngày 6 giờ
Pull request đã merge (30 ngày)
5

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của Eventuous/eventuous

Tất cả issue của Eventuous/eventuous

Issue tương tự

Thêm issue về C#

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.