@@ -2,7 +2,9 @@ package diskqueue
22
33import (
44 "bufio"
5+ "encoding/json"
56 "errors"
7+ "fmt"
68 "io/ioutil"
79 "os"
810 "path"
@@ -12,51 +14,63 @@ import (
1214)
1315
1416type reader struct {
15- file * os.File
16- offset int64
17- reader * bufio.Reader
17+ file * os.File
18+ index int64
19+ offset int64
20+ reader * bufio.Reader
21+ checkpoint checkpoint
22+ }
23+
24+ type checkpoint struct {
25+ Index int64 `json:"index"`
26+ Offset int64 `json:"offset"`
1827}
1928
2029// read data
21- func (r * reader ) read () ([]byte , error ) {
22- // open a new segment
23- if err := r .open (); err != nil {
24- return nil , err
30+ func (r * reader ) read () (int64 , int64 , []byte , error ) {
31+ if err := r .check (); err != nil {
32+ return r .index , r .offset , nil , err
2533 }
2634
2735 // read a line
2836 data , _ , err := r .reader .ReadLine ()
2937 if err != nil {
30- return nil , err
38+ return r . index , r . offset , nil , err
3139 }
3240
3341 r .offset += int64 (len (data )) + 1
34- return data , err
42+ return r . index , r . offset , data , err
3543}
3644
37- // open a new segment
38- func (r * reader ) open () error {
45+ // check a new segment
46+ func (r * reader ) check () error {
3947 if r .file != nil {
4048 return nil
4149 }
4250
43- files , err := r .list ()
51+ file , err := r .next ()
4452 if err != nil {
4553 return err
4654 }
4755
48- // open the earliest segment
49- if r .file , err = os .OpenFile (files [0 ], os .O_RDONLY , Config .FilePerm ); err != nil {
56+ return r .open (file )
57+ }
58+
59+ func (r * reader ) open (file string ) (err error ) {
60+ if r .file , err = os .OpenFile (file , os .O_RDONLY , Config .FilePerm ); err != nil {
5061 return err
5162 }
5263
64+ // get file index
65+ r .index = r .getIndex (file )
66+
5367 // seek read offset
5468 if _ , err = r .file .Seek (r .offset , 0 ); err != nil {
5569 return err
5670 }
5771
5872 r .reader = bufio .NewReader (r .file )
59- return err
73+ return nil
6074}
6175
6276// rotate to next segment
@@ -66,58 +80,76 @@ func (r *reader) rotate() error {
6680 }
6781
6882 // close segment
69- r .file .Close ()
70-
71- // remove segment
72- if err := os .Remove (r .file .Name ()); err != nil {
73- return err
74- }
75-
83+ _ = r .file .Close ()
7684 r .file , r .offset , r .reader = nil , 0 , nil
77- r .sync ()
78-
7985 return nil
8086}
8187
82- // list all segments
83- func (r * reader ) list () ([]string , error ) {
84- files , err := filepath .Glob (filepath .Join (Config .Path , "*.data" ))
85- if err != nil {
86- return nil , err
88+ // close reader
89+ func (r * reader ) close () {
90+ if r .file == nil {
91+ return
8792 }
8893
89- if len ( files ) == 0 {
90- return nil , errors . New ( "empty" )
94+ if err := r . file . Close (); err != nil {
95+ return
9196 }
9297
93- sort .Strings (files )
94- return files , err
98+ r .file , r .reader , r .index , r .offset = nil , nil , 0 , 0
9599}
96100
97- // sync read offset
101+ // sync index and offset
98102func (r * reader ) sync () {
99103 name := path .Join (Config .Path , Config .CheckpointFile )
100- offset := [] byte ( strconv . FormatInt ( r . offset , 10 ) )
101- _ = ioutil .WriteFile (name , offset , Config .FilePerm )
104+ data , _ := json . Marshal ( & r . checkpoint )
105+ _ = ioutil .WriteFile (name , data , Config .FilePerm )
102106}
103107
104- // close reader
105- func (r * reader ) close () {
106- if r .file == nil {
107- return
108+ // restore index and offset
109+ func (r * reader ) restore () (err error ) {
110+ name := path .Join (Config .Path , Config .CheckpointFile )
111+ data , _ := ioutil .ReadFile (name )
112+ _ = json .Unmarshal (data , & r .checkpoint )
113+ r .index , r .offset = r .checkpoint .Index , r .checkpoint .Offset
114+
115+ if r .index == 0 {
116+ return nil
108117 }
109118
110- r .sync ()
111- if err := r .file .Close (); err != nil {
112- return
119+ if err = r .open (fmt .Sprintf ("%s/%d.data" , Config .Path , r .index )); err != nil {
120+ r .offset = 0
113121 }
114122
115- r . file , r . offset , r . reader = nil , 0 , nil
123+ return err
116124}
117125
118- // restore read offset
119- func (r * reader ) restore () {
120- name := path .Join (Config .Path , Config .CheckpointFile )
121- offset , _ := ioutil .ReadFile (name )
122- r .offset , _ = strconv .ParseInt (string (offset ), 10 , 64 )
126+ // next segment
127+ func (r * reader ) next () (string , error ) {
128+ files , err := filepath .Glob (filepath .Join (Config .Path , "*.data" ))
129+ if err != nil {
130+ return "" , err
131+ }
132+
133+ sort .Strings (files )
134+
135+ for _ , file := range files {
136+ index := r .getIndex (file )
137+ if index < r .checkpoint .Index {
138+ _ = os .Remove (file ) // remove expired segment
139+ }
140+
141+ if index > r .index {
142+ return file , nil
143+ }
144+ }
145+
146+ return "" , errors .New ("queue is empty" )
147+ }
148+
149+ // get segment index
150+ func (r * reader ) getIndex (filename string ) int64 {
151+ base := path .Base (filename )
152+ name := base [0 : len (base )- len (path .Ext (filename ))]
153+ index , _ := strconv .ParseInt (name , 10 , 64 )
154+ return index
123155}
0 commit comments