55 "encoding/binary"
66 "fmt"
77 "log/slog"
8+ "math"
89 "time"
910
1011 "github.com/google/uuid"
@@ -27,14 +28,12 @@ func minObjectIDForTimestamp(t time.Time) bson.ObjectID {
2728}
2829
2930// maxObjectIDForTimestamp creates an ObjectID with the given timestamp and
30- // all trailing bytes set to 0xFF, producing the largest possible ObjectID
31- // for that second.
31+ // all trailing bytes set to 0xFF (max unsigned 64-bit value), producing
32+ // the largest possible ObjectID for that second.
3233func maxObjectIDForTimestamp (t time.Time ) bson.ObjectID {
3334 var oid [12 ]byte
3435 binary .BigEndian .PutUint32 (oid [0 :4 ], uint32 (t .Unix ()))
35- for i := 4 ; i < 12 ; i ++ {
36- oid [i ] = 0xFF
37- }
36+ binary .BigEndian .PutUint64 (oid [4 :], math .MaxUint64 )
3837 return oid
3938}
4039
@@ -53,25 +52,10 @@ func (c *MongoConnector) minMaxPartitions(
5352 FullTablePartition : true ,
5453 }}
5554
56- minRaw , err := findBoundaryID (ctx , collection , 1 )
57- if err != nil {
58- c .logger .Info ("[mongo] could not find min _id, collection may be empty, falling back to full table partition" ,
59- slog .String ("error" , err .Error ()))
60- return fullTablePartition , nil
61- }
62- maxRaw , err := findBoundaryID (ctx , collection , - 1 )
55+ minRaw , maxRaw , err := findMinMaxObjectIDs (ctx , collection )
6356 if err != nil {
64- c .logger .Info ("[mongo] could not find max _id, falling back to full table partition" ,
65- slog .String ("error" , err .Error ()))
66- return fullTablePartition , nil
67- }
68-
69- // Given MongoDB's type-ordered _id, if both min and max are ObjectID
70- // then every document in the collection must have an ObjectID _id.
71- if minRaw .Type != bson .TypeObjectID || maxRaw .Type != bson .TypeObjectID {
72- c .logger .Info ("[mongo] _id contains non-ObjectID type, falling back to full table partition" ,
73- slog .String ("minType" , minRaw .Type .String ()),
74- slog .String ("maxType" , maxRaw .Type .String ()))
57+ c .logger .Warn ("[mongo] could not find min/max objectIDs, falling back to full table partition" ,
58+ slog .String ("reason" , err .Error ()))
7559 return fullTablePartition , nil
7660 }
7761
@@ -115,17 +99,50 @@ func (c *MongoConnector) minMaxPartitions(
11599 return partitions , nil
116100}
117101
118- // findBoundaryID returns the raw _id value of the min (direction=1) or max (direction=-1)
119- // document in the collection. Because _id is always indexed, this is an efficient O(log n) operation.
120- // The caller should inspect the returned RawValue.Type to determine the BSON type.
102+ // findMinMaxObjectIDs returns the min and max object IDs from the collection.
103+ // The two boundary queries are not wrapped in a snapshot session: a concurrent
104+ // insert between them may shift the true min/max, but this is safe because the
105+ // change stream resume token is captured before either query runs. Any writes
106+ // that occur during or after partitioning will be replayed by the change stream.
107+ func findMinMaxObjectIDs (
108+ ctx context.Context ,
109+ collection * mongo.Collection ,
110+ ) (bson.RawValue , bson.RawValue , error ) {
111+ minRaw , err := findBoundaryID (ctx , collection , Lower )
112+ if err != nil {
113+ return bson.RawValue {}, bson.RawValue {}, fmt .Errorf ("failed to find min _id: %w" , err )
114+ }
115+
116+ maxRaw , err := findBoundaryID (ctx , collection , Upper )
117+ if err != nil {
118+ return bson.RawValue {}, bson.RawValue {}, fmt .Errorf ("failed to find max _id: %w" , err )
119+ }
120+
121+ // Given MongoDB's type-ordered _id, if both min and max are ObjectID, then every
122+ // document in the collection must have an ObjectID _id.
123+ if minRaw .Type != bson .TypeObjectID || maxRaw .Type != bson .TypeObjectID {
124+ return bson.RawValue {}, bson.RawValue {}, fmt .Errorf (
125+ "_id contains non-ObjectID type (min id type=%T, max id type=%T" , minRaw , maxRaw )
126+ }
127+ return minRaw , maxRaw , nil
128+ }
129+
130+ type Boundary int
131+
132+ const (
133+ Lower Boundary = 1
134+ Upper Boundary = - 1
135+ )
136+
137+ // findBoundaryID returns the lower- or upper-bound _id of a collection
121138func findBoundaryID (
122139 ctx context.Context ,
123140 collection * mongo.Collection ,
124- direction int ,
141+ boundary Boundary ,
125142) (bson.RawValue , error ) {
126143 findCmd := bson.D {
127144 {Key : "find" , Value : collection .Name ()},
128- {Key : "sort" , Value : bson.D {{Key : DefaultDocumentKeyColumnName , Value : direction }}},
145+ {Key : "sort" , Value : bson.D {{Key : DefaultDocumentKeyColumnName , Value : boundary }}},
129146 {Key : "limit" , Value : 1 },
130147 {Key : "projection" , Value : bson.D {{Key : DefaultDocumentKeyColumnName , Value : 1 }}},
131148 }
0 commit comments