@@ -2,13 +2,19 @@ package fracmanager
22
33import (
44 "context"
5+ "io"
6+ "math/rand"
7+ "path/filepath"
8+ "time"
59
10+ "github.com/oklog/ulid/v2"
611 "github.com/prometheus/client_golang/prometheus"
712 "github.com/prometheus/client_golang/prometheus/promauto"
813
914 "github.com/ozontech/seq-db/frac"
1015 "github.com/ozontech/seq-db/frac/common"
1116 "github.com/ozontech/seq-db/frac/sealed"
17+ "github.com/ozontech/seq-db/frac/sealed/sealing"
1218 "github.com/ozontech/seq-db/storage"
1319 "github.com/ozontech/seq-db/storage/s3"
1420)
@@ -19,27 +25,28 @@ var storeBytesRead = promauto.NewCounter(prometheus.CounterOpts{
1925 Name : "bytes_read" ,
2026})
2127
28+ // fractionProvider is a factory for creating different types of fractions
29+ // Contains all necessary dependencies for creating and managing fractions
2230type fractionProvider struct {
23- s3cli * s3.Client
24- config * frac.Config
25- cacheProvider * CacheMaintainer
26- activeIndexer * frac.ActiveIndexer
27- readLimiter * storage.ReadLimiter
31+ s3cli * s3.Client // Client for S3 storage operations
32+ config * Config // Fraction manager configuration
33+ cacheProvider * CacheMaintainer // Cache provider for data access optimization
34+ activeIndexer * frac.ActiveIndexer // Indexer for active fractions
35+ readLimiter * storage.ReadLimiter // Read rate limiter
36+ ulidEntropy io.Reader // Entropy source for ULID generation
2837}
2938
3039func newFractionProvider (
31- c * frac. Config , s3cli * s3.Client , cp * CacheMaintainer ,
32- readerWorkers , indexWorkers int ,
40+ cfg * Config , s3cli * s3.Client , cp * CacheMaintainer ,
41+ readLimiter * storage. ReadLimiter , indexer * frac. ActiveIndexer ,
3342) * fractionProvider {
34- ai := frac .NewActiveIndexer (indexWorkers , indexWorkers )
35- ai .Start () // first start indexWorkers to allow active frac replaying
36-
3743 return & fractionProvider {
3844 s3cli : s3cli ,
39- config : c ,
45+ config : cfg ,
4046 cacheProvider : cp ,
41- activeIndexer : ai ,
42- readLimiter : storage .NewReadLimiter (readerWorkers , storeBytesRead ),
47+ activeIndexer : indexer ,
48+ readLimiter : readLimiter ,
49+ ulidEntropy : ulid .Monotonic (rand .New (rand .NewSource (time .Now ().UnixNano ())), 0 ),
4350 }
4451}
4552
@@ -50,7 +57,7 @@ func (fp *fractionProvider) NewActive(name string) *frac.Active {
5057 fp .readLimiter ,
5158 fp .cacheProvider .CreateDocBlockCache (),
5259 fp .cacheProvider .CreateSortDocsCache (),
53- fp .config ,
60+ & fp .config . Fraction ,
5461 )
5562}
5663
@@ -60,37 +67,75 @@ func (fp *fractionProvider) NewSealed(name string, cachedInfo *common.Info) *fra
6067 fp .readLimiter ,
6168 fp .cacheProvider .CreateIndexCache (),
6269 fp .cacheProvider .CreateDocBlockCache (),
63- cachedInfo ,
64- fp .config ,
70+ cachedInfo , // Preloaded meta information
71+ & fp .config . Fraction ,
6572 )
6673}
6774
6875func (fp * fractionProvider ) NewSealedPreloaded (name string , preloadedData * sealed.PreloadedData ) * frac.Sealed {
6976 return frac .NewSealedPreloaded (
7077 name ,
71- preloadedData ,
78+ preloadedData , // Data already loaded into memory
7279 fp .readLimiter ,
7380 fp .cacheProvider .CreateIndexCache (),
7481 fp .cacheProvider .CreateDocBlockCache (),
75- fp .config ,
82+ & fp .config . Fraction ,
7683 )
7784}
7885
79- func (fp * fractionProvider ) NewRemote (
80- ctx context.Context , name string , cachedInfo * common.Info ,
81- ) * frac.Remote {
86+ func (fp * fractionProvider ) NewRemote (ctx context.Context , name string , cachedInfo * common.Info ) * frac.Remote {
8287 return frac .NewRemote (
8388 ctx ,
8489 name ,
8590 fp .readLimiter ,
8691 fp .cacheProvider .CreateIndexCache (),
8792 fp .cacheProvider .CreateDocBlockCache (),
8893 cachedInfo ,
89- fp .config ,
94+ & fp .config . Fraction ,
9095 fp .s3cli ,
9196 )
9297}
9398
94- func (fp * fractionProvider ) Stop () {
95- fp .activeIndexer .Stop ()
99+ // nextFractionID generates a unique identifier for a new fraction
100+ // IMPORTANT: This method is not thread-safe. When used in concurrent environments,
101+ // external synchronization must be provided to avoid ID collisions
102+ func (fp * fractionProvider ) nextFractionID () string {
103+ return ulid .MustNew (ulid .Timestamp (time .Now ()), fp .ulidEntropy ).String ()
104+ }
105+
106+ // CreateActive creates a new active fraction with auto-generated filename
107+ // Filename pattern: base_pattern + ULID
108+ func (fp * fractionProvider ) CreateActive () * frac.Active {
109+ filePath := fileBasePattern + fp .nextFractionID ()
110+ baseFilePath := filepath .Join (fp .config .DataDir , filePath )
111+ return fp .NewActive (baseFilePath )
112+ }
113+
114+ // Seal converts an active fraction to a sealed one
115+ // Process includes sorting, indexing, and data optimization for reading
116+ func (fp * fractionProvider ) Seal (active * frac.Active ) (* frac.Sealed , error ) {
117+ src , err := frac .NewActiveSealingSource (active , fp .config .SealParams )
118+ if err != nil {
119+ return nil , err
120+ }
121+ preloaded , err := sealing .Seal (src , fp .config .SealParams )
122+ if err != nil {
123+ return nil , err
124+ }
125+
126+ return fp .NewSealedPreloaded (active .BaseFileName , preloaded ), nil
127+ }
128+
129+ // Offload uploads fraction to S3 storage and returns a remote fraction
130+ // IMPORTANT: context controls timeouts and operation cancellation
131+ func (fp * fractionProvider ) Offload (ctx context.Context , f frac.Fraction ) (* frac.Remote , error ) {
132+ mustBeOffloaded , err := f .Offload (ctx , s3 .NewUploader (fp .s3cli ))
133+ if err != nil {
134+ return nil , err
135+ }
136+ if ! mustBeOffloaded {
137+ return nil , nil
138+ }
139+ info := f .Info ()
140+ return fp .NewRemote (ctx , info .Path , info ), nil
96141}
0 commit comments