@@ -18,31 +18,73 @@ import (
18
18
const (
19
19
// defaultFlushInterval is default time interval between successive flushes.
20
20
defaultFlushInterval = 10 * time .Second
21
+ // defaultWorkerCount is a default number of workers that flush objects.
22
+ defaultWorkerCount = 20
21
23
)
22
24
23
25
// runFlushLoop starts background workers which periodically flush objects to the blobstor.
24
26
func (c * cache ) runFlushLoop () {
27
+ for i := range c .workersCount {
28
+ c .wg .Add (1 )
29
+ go c .flushWorker (i )
30
+ }
31
+
25
32
c .wg .Add (1 )
26
- go func () {
27
- defer c .wg .Done ()
28
- tick := time .NewTicker (defaultFlushInterval )
29
- for {
30
- select {
31
- case <- tick .C :
32
- c .modeMtx .RLock ()
33
- if c .readOnly () {
34
- c .modeMtx .RUnlock ()
35
- break
36
- }
33
+ go c .flushScheduler ()
34
+ }
37
35
38
- _ = c .flush (true )
36
+ func (c * cache ) flushScheduler () {
37
+ defer c .wg .Done ()
38
+ ticker := time .NewTicker (defaultFlushInterval )
39
+ defer ticker .Stop ()
39
40
41
+ for {
42
+ select {
43
+ case <- ticker .C :
44
+ c .modeMtx .RLock ()
45
+ if c .readOnly () {
40
46
c .modeMtx .RUnlock ()
41
- case <- c .closeCh :
47
+ continue
48
+ }
49
+ c .modeMtx .RUnlock ()
50
+ err := c .fsTree .IterateAddresses (func (addr oid.Address ) error {
51
+ select {
52
+ case c .flushCh <- addr :
53
+ return nil
54
+ case <- c .closeCh :
55
+ return errors .New ("closed during iteration" )
56
+ }
57
+ }, true )
58
+ if err != nil {
59
+ c .log .Warn ("iteration failed" , zap .Error (err ))
60
+ }
61
+ case <- c .closeCh :
62
+ return
63
+ }
64
+ }
65
+ }
66
+
67
+ func (c * cache ) flushWorker (id int ) {
68
+ defer c .wg .Done ()
69
+ for {
70
+ select {
71
+ case addr , ok := <- c .flushCh :
72
+ if ! ok {
42
73
return
43
74
}
75
+ c .modeMtx .RLock ()
76
+ if c .readOnly () {
77
+ c .modeMtx .RUnlock ()
78
+ continue
79
+ }
80
+ if err := c .flushSingle (addr , true ); err != nil {
81
+ c .log .Warn ("flush failed" , zap .Int ("worker" , id ), zap .Error (err ))
82
+ }
83
+ c .modeMtx .RUnlock ()
84
+ case <- c .closeCh :
85
+ return
44
86
}
45
- }()
87
+ }
46
88
}
47
89
48
90
func (c * cache ) reportFlushError (msg string , addr string , err error ) {
@@ -55,56 +97,52 @@ func (c *cache) reportFlushError(msg string, addr string, err error) {
55
97
}
56
98
}
57
99
58
- func (c * cache ) flush (ignoreErrors bool ) error {
59
- var addrHandler = func (addr oid.Address ) error {
60
- sAddr := addr .EncodeToString ()
61
-
62
- data , err := c .fsTree .GetBytes (addr )
63
- if err != nil {
64
- if errors .As (err , new (apistatus.ObjectNotFound )) {
65
- // an object can be removed b/w iterating over it
66
- // and reading its payload; not an error
67
- return nil
68
- }
100
+ func (c * cache ) flushSingle (addr oid.Address , ignoreErrors bool ) error {
101
+ sAddr := addr .EncodeToString ()
69
102
70
- c .reportFlushError ("can't read a file" , sAddr , err )
71
- if ignoreErrors {
72
- return nil
73
- }
74
- return err
103
+ data , err := c .fsTree .GetBytes (addr )
104
+ if err != nil {
105
+ if errors .As (err , new (apistatus.ObjectNotFound )) {
106
+ // an object can be removed b/w iterating over it
107
+ // and reading its payload; not an error
108
+ return nil
75
109
}
76
110
77
- var obj object.Object
78
- err = obj .Unmarshal (data )
79
- if err != nil {
80
- c .reportFlushError ("can't unmarshal an object" , sAddr , err )
81
- if ignoreErrors {
82
- return nil
83
- }
84
- return err
111
+ c .reportFlushError ("can't read a file" , sAddr , err )
112
+ if ignoreErrors {
113
+ return nil
85
114
}
115
+ return err
116
+ }
86
117
87
- err = c .flushObject (& obj , data )
88
- if err != nil {
89
- return err
118
+ var obj object.Object
119
+ err = obj .Unmarshal (data )
120
+ if err != nil {
121
+ c .reportFlushError ("can't unmarshal an object" , sAddr , err )
122
+ if ignoreErrors {
123
+ return nil
90
124
}
125
+ return err
126
+ }
91
127
92
- err = c .fsTree .Delete (addr )
93
- if err != nil && ! errors .As (err , new (apistatus.ObjectNotFound )) {
94
- c .log .Error ("can't remove object from write-cache" , zap .Error (err ))
95
- } else if err == nil {
96
- storagelog .Write (c .log ,
97
- storagelog .AddressField (addr ),
98
- storagelog .StorageTypeField (wcStorageType ),
99
- storagelog .OpField ("DELETE" ),
100
- )
101
- c .objCounters .Delete (addr )
102
- }
128
+ err = c .flushObject (& obj , data )
129
+ if err != nil {
130
+ return err
131
+ }
103
132
104
- return nil
133
+ err = c .fsTree .Delete (addr )
134
+ if err != nil && ! errors .As (err , new (apistatus.ObjectNotFound )) {
135
+ c .log .Error ("can't remove object from write-cache" , zap .Error (err ))
136
+ } else if err == nil {
137
+ storagelog .Write (c .log ,
138
+ storagelog .AddressField (addr ),
139
+ storagelog .StorageTypeField (wcStorageType ),
140
+ storagelog .OpField ("DELETE" ),
141
+ )
142
+ c .objCounters .Delete (addr )
105
143
}
106
144
107
- return c . fsTree . IterateAddresses ( addrHandler , ignoreErrors )
145
+ return nil
108
146
}
109
147
110
148
// flushObject is used to write object directly to the main storage.
@@ -150,3 +188,9 @@ func (c *cache) Flush(ignoreErrors bool) error {
150
188
151
189
return c .flush (ignoreErrors )
152
190
}
191
+
192
+ func (c * cache ) flush (ignoreErrors bool ) error {
193
+ return c .fsTree .IterateAddresses (func (addr oid.Address ) error {
194
+ return c .flushSingle (addr , ignoreErrors )
195
+ }, ignoreErrors )
196
+ }
0 commit comments