I'm trying to understand a problem I have run into that I don't believe should be possible when dealing with transactions utilizing the read committed isolation level. I have a table that is being used as a queue. In one thread (connection 1) I insert multiple batches of 20 records into each table. Each batch of 20 records is performed inside a transaction. In a second thread (connection 2) I perform an update to change the status of the records that have been inserted into the queue, which also occurs inside a transaction. When running concurrently, it is my expectation that the number of rows affected by the update (connection 2) should be a multiple of 20, since connection 1 is inserting rows in the table inserts in batches of 20 rows within a transaction. 

But my testing shows this is not always the case, and on occasion I'm able to update a subset of records from connection 1's batch. Should this be possible or am I missing something about transactions, concurrency, and isolation levels? Below is a set of test scripts I created to reproduce this issue in T-SQL.

***This script inserts 20,000 records into the table in transaction batches of 20.***

```
USE ReadTest
GO

SET TRANSACTION ISOLATION LEVEL READ COMMITTED
GO

SET NOCOUNT ON

DECLARE @trans_id INTEGER
DECLARE @cmd_id INTEGER
DECLARE @text_str VARCHAR(4000)

SET @trans_id = 0
SET @text_str = 'Placeholder String Value'                

-- First empty the table
DELETE FROM TABLE_A

WHILE @trans_id < 1000 BEGIN
    SET @trans_id = @trans_id + 1
    SET @cmd_id = 0

    BEGIN TRANSACTION
--  Insert 20 records into the table per transaction
    WHILE @cmd_id < 20 BEGIN
        SET @cmd_id = @cmd_id + 1

        INSERT INTO TABLE_A ( transaction_id, command_id, [type], status, text_field ) 
            VALUES ( @trans_id, @cmd_id, 1, 1,  @text_str )
    END             
    COMMIT

END

PRINT 'DONE'

```

***This script updates the records in the table, changing the status from 1 to 2 and then checks the rowcount from the update operation. When the rowcount is not a multiple of 20, and print statement indicates this and the number of rows affected.***

```
USE ReadTest
GO

SET TRANSACTION ISOLATION LEVEL READ COMMITTED
GO

SET NOCOUNT ON
DECLARE @loop_counter INTEGER
DECLARE @trans_id INTEGER
DECLARE @count INTEGER

SET @loop_counter = 0

WHILE @loop_counter < 100000 BEGIN

    SET @loop_counter = @loop_counter + 1
    BEGIN TRANSACTION
        UPDATE TABLE_A SET status = 2 
        WHERE status = 1
            and type = 1
        SET @count = @@ROWCOUNT
    COMMIT

    IF ( @count % 20 <> 0 ) BEGIN
--      Records in concurrent transaction inserting in batches of 20 records before commit.
        PRINT '*** Rowcount not a multiple of 20. Count = ' + CAST(@count AS VARCHAR) + ' ***'
    END

    IF @count > 0 BEGIN
--      Delete the records where the status was changed.
        DELETE TABLE_A WHERE status = 2
    END
END

PRINT 'DONE'

```

***This script creates the test queue table in a new database called ReadTest.***

```
USE master;
GO

IF EXISTS (SELECT * FROM sys.databases WHERE name = 'ReadTest')
  BEGIN;
  DROP DATABASE ReadTest;
  END;
GO

CREATE DATABASE ReadTest;
GO

ALTER DATABASE ReadTest
SET ALLOW_SNAPSHOT_ISOLATION OFF
GO

ALTER DATABASE ReadTest
SET READ_COMMITTED_SNAPSHOT OFF
GO

USE ReadTest
GO

CREATE TABLE [dbo].[TABLE_A](
    [ROWGUIDE] [uniqueidentifier] NOT NULL,
    [TRANSACTION_ID] [int] NOT NULL,
    [COMMAND_ID] [int] NOT NULL,
    [TYPE] [int] NOT NULL,
    [STATUS] [int] NOT NULL,
    [TEXT_FIELD] [varchar](4000) NULL
 CONSTRAINT [PK_TABLE_A] PRIMARY KEY NONCLUSTERED 
(
    [ROWGUIDE] ASC
) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE [dbo].[TABLE_A] ADD  DEFAULT (newsequentialid()) FOR [ROWGUIDE]
GO

```