Skip to content

Commit 06d90e2

Browse files
authored
release gc cancel (#25)
release gc cancel
1 parent c523d09 commit 06d90e2

File tree

5 files changed

+77
-21
lines changed

5 files changed

+77
-21
lines changed

gobeansdb/web.go

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"runtime/debug"
1111
"strconv"
1212

13-
yaml "gopkg.in/yaml.v2"
13+
"gopkg.in/yaml.v2"
1414

1515
"github.com/douban/gobeansdb/cmem"
1616
"github.com/douban/gobeansdb/config"
@@ -55,7 +55,6 @@ func init() {
5555

5656
http.HandleFunc("/statgetset", handleStatGetSet)
5757
http.HandleFunc("/freememory", handleFreeMemory)
58-
5958
}
6059

6160
func initWeb() {
@@ -314,6 +313,15 @@ func handleGC(w http.ResponseWriter, r *http.Request) {
314313
var err error
315314
var bucketID int64
316315
var pretend bool
316+
317+
isQuery := getGCQuery(r)
318+
if isQuery {
319+
gcResult := storage.hstore.GCBuckets()
320+
w.Header().Set("Content-Type", "application/json")
321+
json.NewEncoder(w).Encode(gcResult)
322+
return
323+
}
324+
317325
defer func() {
318326
if err != nil {
319327
e := fmt.Sprintf("<p> err : %s </p>", err.Error())
@@ -328,17 +336,16 @@ func handleGC(w http.ResponseWriter, r *http.Request) {
328336
}
329337
}()
330338

331-
isQuery := getGCQuery(r)
332-
if isQuery {
333-
result = storage.hstore.GCBuckets()
339+
bucketID, err = getBucket(r)
340+
if err != nil {
334341
return
335342
}
343+
bktID := int(bucketID)
336344

337-
bucketID, err = getBucket(r)
345+
err = r.ParseForm()
338346
if err != nil {
339347
return
340348
}
341-
r.ParseForm()
342349
start, err := getFormValueInt(r, "start", -1)
343350
if err != nil {
344351
return
@@ -353,16 +360,27 @@ func handleGC(w http.ResponseWriter, r *http.Request) {
353360
}
354361

355362
s := r.FormValue("run")
356-
pretend = (s != "true")
363+
pretend = s != "true"
357364

358365
s = r.FormValue("merge")
359-
merge := (s == "true")
366+
merge := s == "true"
367+
368+
s = r.FormValue("cancel")
369+
gcCancel := s == "true"
360370

361-
start, end, err = storage.hstore.GC(int(bucketID), start, end, noGCDays, merge, pretend)
362-
if err == nil {
371+
if gcCancel {
372+
src, dst := storage.hstore.CancelGC(bktID)
373+
if src == -1 {
374+
result = fmt.Sprintf("bucket %d is not gcing", bktID)
375+
} else {
376+
result = fmt.Sprintf("cancel gc on bucket %d, src: %d -> dst: %d", bktID, src, dst)
377+
}
378+
} else {
379+
start, end, err = storage.hstore.GC(bktID, start, end, noGCDays, merge, pretend)
363380
result = fmt.Sprintf("<p/> bucket %d, start %d, end %d, merge %v, pretend %v <p/>",
364381
bucketID, start, end, merge, pretend)
365382
}
383+
return
366384
}
367385

368386
func handleDU(w http.ResponseWriter, r *http.Request) {

store/data.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ func (ds *dataStore) flush(chunk int, force bool) error {
132132
filessize, w.offset, ds.genPath(chunk), &ds.chunks[chunk])
133133
}
134134
nflushed, err := ds.chunks[chunk].flush(w, false)
135+
ds.Lock()
135136
ds.wbufSize -= nflushed
137+
ds.Unlock()
136138
w.Close()
137139

138140
return nil

store/gc.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ type GCState struct {
2525
Src int
2626
Dst int
2727

28-
Err error
28+
Err error
29+
CancelFlag bool
2930
// sum
3031
GCFileState
3132
}
@@ -210,10 +211,20 @@ func (mgr *GCMgr) gc(bkt *Bucket, startChunkID, endChunkID int, merge bool) {
210211
defer mgr.AfterBucket(bkt)
211212

212213
gc.Dst = startChunkID
213-
for i := 0; i < startChunkID; i++ {
214+
// try to find the nearest chunk that small than start chunk
215+
for i := startChunkID - 1; i >= 0; i-- {
214216
sz := bkt.datas.chunks[i].size
215-
if sz > 0 && (int64(sz) < Conf.DataFileMax-config.MCConf.BodyMax) {
216-
gc.Dst = i
217+
if sz > 0 {
218+
if int64(sz) < Conf.DataFileMax-config.MCConf.BodyMax {
219+
// previous one available and not full, use it.
220+
gc.Dst = i
221+
break
222+
} else {
223+
if i < startChunkID-1 { // not previous one
224+
gc.Dst = i + 1
225+
}
226+
break
227+
}
217228
}
218229
}
219230

@@ -230,6 +241,10 @@ func (mgr *GCMgr) gc(bkt *Bucket, startChunkID, endChunkID int, merge bool) {
230241
}()
231242

232243
for gc.Src = gc.Begin; gc.Src <= gc.End; gc.Src++ {
244+
if gc.CancelFlag {
245+
logger.Infof("GC canceled: src %d dst %d", gc.Src, gc.Dst)
246+
return
247+
}
233248
if bkt.datas.chunks[gc.Src].size <= 0 {
234249
logger.Infof("skip empty chunk %d", gc.Src)
235250
continue

store/hstore.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"os"
77
"path/filepath"
88
"strconv"
9-
"strings"
109
"sync"
1110
"sync/atomic"
1211
"time"
@@ -20,6 +19,7 @@ import (
2019
var (
2120
logger = loghub.ErrorLogger
2221
mergeChan chan int
22+
gcLock sync.Mutex
2323
)
2424

2525
type HStore struct {
@@ -246,15 +246,15 @@ func (store *HStore) ListDir(ki *KeyInfo) ([]byte, error) {
246246
return store.ListUpper(ki)
247247
}
248248

249-
func (store *HStore) GCBuckets() string {
250-
var result strings.Builder
249+
func (store *HStore) GCBuckets() []string {
250+
251251
store.gcMgr.mu.Lock()
252+
result := make([]string, 0, len(store.gcMgr.stat))
252253
for k := range store.gcMgr.stat {
253-
result.WriteString(strconv.FormatInt(int64(k), 16))
254-
result.WriteString(",")
254+
result = append(result, strconv.FormatInt(int64(k), 16))
255255
}
256256
store.gcMgr.mu.Unlock()
257-
return strings.TrimSuffix(result.String(), ",")
257+
return result
258258
}
259259

260260
func (store *HStore) GC(bucketID, beginChunkID, endChunkID, noGCDays int, merge, pretend bool) (begin, end int, err error) {
@@ -288,10 +288,30 @@ func (store *HStore) GC(bucketID, beginChunkID, endChunkID, noGCDays int, merge,
288288
if pretend {
289289
return
290290
}
291+
291292
go store.gcMgr.gc(bkt, begin, end, merge)
292293
return
293294
}
294295

296+
func (store *HStore) CancelGC(bucketID int) (src, dst int) {
297+
// prevent same bucket concurrent request
298+
gcLock.Lock()
299+
defer gcLock.Unlock()
300+
301+
// will delete key at goroutine in store/gc.go
302+
store.gcMgr.mu.RLock()
303+
stat, exists := store.gcMgr.stat[bucketID]
304+
store.gcMgr.mu.RUnlock()
305+
306+
if exists {
307+
stat.CancelFlag = true
308+
src, dst = stat.Src, stat.Dst
309+
} else {
310+
src, dst = -1, -1
311+
}
312+
return
313+
}
314+
295315
func (store *HStore) IsGCRunning() bool {
296316
var result bool
297317
store.gcMgr.mu.RLock()

store/hstore_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,7 @@ func testGC(t *testing.T, casefunc testGCFunc, name string, numRecPerFile int) {
10011001
store.Close()
10021002
checkAllDataWithHints(bucketDir)
10031003
}
1004+
10041005
func TestGCConcurrency(t *testing.T) {
10051006
testGCConcurrency(t, 10000)
10061007
}

0 commit comments

Comments
 (0)