@@ -30,7 +30,9 @@ type SFTPStoreBase struct {
30
30
31
31
// SFTPStore is a chunk store that uses SFTP over SSH.
32
32
type SFTPStore struct {
33
- * SFTPStoreBase
33
+ pool chan * SFTPStoreBase
34
+ location * url.URL
35
+ n int
34
36
}
35
37
36
38
// Creates a base sftp client
@@ -117,19 +119,37 @@ func (s *SFTPStoreBase) String() string {
117
119
return s .location .String ()
118
120
}
119
121
122
+ // Returns the path for a chunk
123
+ func (s * SFTPStoreBase ) nameFromID (id ChunkID ) string {
124
+ sID := id .String ()
125
+ name := s .path + sID [0 :4 ] + "/" + sID
126
+ if s .opt .Uncompressed {
127
+ name += UncompressedChunkExt
128
+ } else {
129
+ name += CompressedChunkExt
130
+ }
131
+ return name
132
+ }
133
+
120
134
// NewSFTPStore initializes a chunk store using SFTP over SSH.
121
135
func NewSFTPStore (location * url.URL , opt StoreOptions ) (* SFTPStore , error ) {
122
- b , err := newSFTPStoreBase (location , opt )
123
- if err != nil {
124
- return nil , err
136
+ s := & SFTPStore {make (chan * SFTPStoreBase , opt .N ), location , opt .N }
137
+ for i := 0 ; i < opt .N ; i ++ {
138
+ c , err := newSFTPStoreBase (location , opt )
139
+ if err != nil {
140
+ return nil , err
141
+ }
142
+ s .pool <- c
125
143
}
126
- return & SFTPStore { b } , nil
144
+ return s , nil
127
145
}
128
146
129
147
// GetChunk returns a chunk from an SFTP store, returns ChunkMissing if the file does not exist
130
148
func (s * SFTPStore ) GetChunk (id ChunkID ) (* Chunk , error ) {
131
- name := s .nameFromID (id )
132
- f , err := s .client .Open (name )
149
+ c := <- s .pool
150
+ defer func () { s .pool <- c }()
151
+ name := c .nameFromID (id )
152
+ f , err := c .client .Open (name )
133
153
if err != nil {
134
154
if os .IsNotExist (err ) {
135
155
err = ChunkMissing {id }
@@ -141,51 +161,59 @@ func (s *SFTPStore) GetChunk(id ChunkID) (*Chunk, error) {
141
161
if err != nil {
142
162
return nil , errors .Wrapf (err , "unable to read from %s" , name )
143
163
}
144
- if s .opt .Uncompressed {
145
- return NewChunkWithID (id , b , nil , s .opt .SkipVerify )
164
+ if c .opt .Uncompressed {
165
+ return NewChunkWithID (id , b , nil , c .opt .SkipVerify )
146
166
}
147
- return NewChunkWithID (id , nil , b , s .opt .SkipVerify )
167
+ return NewChunkWithID (id , nil , b , c .opt .SkipVerify )
148
168
}
149
169
150
170
// RemoveChunk deletes a chunk, typically an invalid one, from the filesystem.
151
171
// Used when verifying and repairing caches.
152
172
func (s * SFTPStore ) RemoveChunk (id ChunkID ) error {
153
- name := s .nameFromID (id )
154
- if _ , err := s .client .Stat (name ); err != nil {
173
+ c := <- s .pool
174
+ defer func () { s .pool <- c }()
175
+ name := c .nameFromID (id )
176
+ if _ , err := c .client .Stat (name ); err != nil {
155
177
return ChunkMissing {id }
156
178
}
157
- return s .client .Remove (name )
179
+ return c .client .Remove (name )
158
180
}
159
181
160
182
// StoreChunk adds a new chunk to the store
161
183
func (s * SFTPStore ) StoreChunk (chunk * Chunk ) error {
162
- name := s .nameFromID (chunk .ID ())
184
+ c := <- s .pool
185
+ defer func () { s .pool <- c }()
186
+ name := c .nameFromID (chunk .ID ())
163
187
var (
164
188
b []byte
165
189
err error
166
190
)
167
- if s .opt .Uncompressed {
191
+ if c .opt .Uncompressed {
168
192
b , err = chunk .Uncompressed ()
169
193
} else {
170
194
b , err = chunk .Compressed ()
171
195
}
172
196
if err != nil {
173
197
return err
174
198
}
175
- return s .StoreObject (name , bytes .NewReader (b ))
199
+ return c .StoreObject (name , bytes .NewReader (b ))
176
200
}
177
201
178
202
// HasChunk returns true if the chunk is in the store
179
203
func (s * SFTPStore ) HasChunk (id ChunkID ) bool {
180
- name := s .nameFromID (id )
181
- _ , err := s .client .Stat (name )
204
+ c := <- s .pool
205
+ defer func () { s .pool <- c }()
206
+ name := c .nameFromID (id )
207
+ _ , err := c .client .Stat (name )
182
208
return err == nil
183
209
}
184
210
185
211
// Prune removes any chunks from the store that are not contained in a list
186
212
// of chunks
187
213
func (s * SFTPStore ) Prune (ctx context.Context , ids map [ChunkID ]struct {}) error {
188
- walker := s .client .Walk (s .path )
214
+ c := <- s .pool
215
+ defer func () { s .pool <- c }()
216
+ walker := c .client .Walk (c .path )
189
217
190
218
for walker .Step () {
191
219
// See if we're meant to stop
@@ -207,7 +235,7 @@ func (s *SFTPStore) Prune(ctx context.Context, ids map[ChunkID]struct{}) error {
207
235
}
208
236
// Skip compressed chunks if this is running in uncompressed mode and vice-versa
209
237
var sID string
210
- if s .opt .Uncompressed {
238
+ if c .opt .Uncompressed {
211
239
if ! strings .HasSuffix (path , UncompressedChunkExt ) {
212
240
return nil
213
241
}
@@ -235,13 +263,16 @@ func (s *SFTPStore) Prune(ctx context.Context, ids map[ChunkID]struct{}) error {
235
263
return nil
236
264
}
237
265
238
- func (s * SFTPStore ) nameFromID (id ChunkID ) string {
239
- sID := id .String ()
240
- name := s .path + sID [0 :4 ] + "/" + sID
241
- if s .opt .Uncompressed {
242
- name += UncompressedChunkExt
243
- } else {
244
- name += CompressedChunkExt
266
+ // Close terminates all client connections
267
+ func (s * SFTPStore ) Close () error {
268
+ var err error
269
+ for i := 0 ; i < s .n ; i ++ {
270
+ c := <- s .pool
271
+ err = c .Close ()
245
272
}
246
- return name
273
+ return err
274
+ }
275
+
276
+ func (s * SFTPStore ) String () string {
277
+ return s .location .String ()
247
278
}
0 commit comments