committed by
GitHub
26 changed files with 1050 additions and 45 deletions
-
21Dockerfile
-
3README.md
-
18docker/Dockerfile
-
23docker/docker-compose.yml
-
34docker/entrypoint.sh
-
7weed/command/compact.go
-
2weed/command/export.go
-
6weed/command/filer.go
-
4weed/command/server.go
-
2weed/filer/cassandra_store/cassandra_store.go
-
4weed/filer/embedded_filer/files_in_leveldb.go
-
6weed/filer/filer.go
-
67weed/filer/mysql_store/README.md
-
270weed/filer/mysql_store/mysql_store.go
-
30weed/filer/mysql_store/mysql_store_test.go
-
4weed/filer/redis_store/redis_store.go
-
42weed/server/filer_server.go
-
3weed/server/filer_server_handlers_read.go
-
220weed/server/filer_server_handlers_write.go
-
2weed/storage/compact_map.go
-
3weed/storage/volume.go
-
1weed/storage/volume_checking.go
-
224weed/storage/volume_vacuum.go
-
55weed/storage/volume_vacuum_test.go
-
17weed/topology/data_node.go
-
13weed/topology/volume_layout.go
@ -1,21 +0,0 @@ |
|||||
FROM progrium/busybox |
|
||||
|
|
||||
WORKDIR /opt/weed |
|
||||
|
|
||||
RUN opkg-install curl |
|
||||
RUN echo tlsv1 >> ~/.curlrc |
|
||||
|
|
||||
RUN \ |
|
||||
curl -Lks https://bintray.com$(curl -Lk http://bintray.com/chrislusf/seaweedfs/seaweedfs/_latestVersion | grep linux_amd64.tar.gz | sed -n "/href/ s/.*href=['\"]\([^'\"]*\)['\"].*/\1/gp") | gunzip | tar -xf - -C /opt/weed/ && \ |
|
||||
mkdir ./bin && mv weed_*/* ./bin && \ |
|
||||
chmod +x ./bin/weed |
|
||||
|
|
||||
EXPOSE 8080 |
|
||||
EXPOSE 9333 |
|
||||
|
|
||||
VOLUME /data |
|
||||
|
|
||||
ENV WEED_HOME /opt/weed |
|
||||
ENV PATH ${PATH}:${WEED_HOME}/bin |
|
||||
|
|
||||
ENTRYPOINT ["weed"] |
|
||||
@ -0,0 +1,18 @@ |
|||||
|
FROM progrium/busybox |
||||
|
|
||||
|
RUN opkg-install curl |
||||
|
RUN echo tlsv1 >> ~/.curlrc |
||||
|
|
||||
|
RUN curl -Lks https://bintray.com$(curl -Lk http://bintray.com/chrislusf/seaweedfs/seaweedfs/_latestVersion | grep linux_amd64.tar.gz | sed -n "/href/ s/.*href=['\"]\([^'\"]*\)['\"].*/\1/gp") | gunzip | tar -xf - && \ |
||||
|
mv go_*amd64/weed /usr/bin/ && \ |
||||
|
rm -r go_*amd64 |
||||
|
|
||||
|
EXPOSE 8080 |
||||
|
EXPOSE 9333 |
||||
|
|
||||
|
VOLUME /data |
||||
|
|
||||
|
COPY entrypoint.sh /entrypoint.sh |
||||
|
RUN chmod +x /entrypoint.sh |
||||
|
|
||||
|
ENTRYPOINT ["/entrypoint.sh"] |
||||
@ -0,0 +1,23 @@ |
|||||
|
version: '2' |
||||
|
|
||||
|
services: |
||||
|
master: |
||||
|
image: chrislusf/seaweedfs |
||||
|
ports: |
||||
|
- 9333:9333 |
||||
|
command: "master" |
||||
|
networks: |
||||
|
default: |
||||
|
aliases: |
||||
|
- seaweed_master |
||||
|
volume: |
||||
|
image: chrislusf/seaweedfs |
||||
|
ports: |
||||
|
- 8080:8080 |
||||
|
command: 'volume -max=5 -mserver="master:9333" -port=8080' |
||||
|
depends_on: |
||||
|
- master |
||||
|
networks: |
||||
|
default: |
||||
|
aliases: |
||||
|
- seaweed_volume |
||||
@ -0,0 +1,34 @@ |
|||||
|
#!/bin/sh |
||||
|
|
||||
|
case "$1" in |
||||
|
|
||||
|
'master') |
||||
|
ARGS="-ip `hostname -i` -mdir /data" |
||||
|
# Is this instance linked with an other master? (Docker commandline "--link master1:master") |
||||
|
if [ -n "$MASTER_PORT_9333_TCP_ADDR" ] ; then |
||||
|
ARGS="$ARGS -peers=$MASTER_PORT_9333_TCP_ADDR:$MASTER_PORT_9333_TCP_PORT" |
||||
|
fi |
||||
|
exec /usr/bin/weed $@ $ARGS |
||||
|
;; |
||||
|
|
||||
|
'volume') |
||||
|
ARGS="-ip `hostname -i` -dir /data" |
||||
|
# Is this instance linked with a master? (Docker commandline "--link master1:master") |
||||
|
if [ -n "$MASTER_PORT_9333_TCP_ADDR" ] ; then |
||||
|
ARGS="$ARGS -mserver=$MASTER_PORT_9333_TCP_ADDR:$MASTER_PORT_9333_TCP_PORT" |
||||
|
fi |
||||
|
exec /usr/bin/weed $@ $ARGS |
||||
|
;; |
||||
|
|
||||
|
'server') |
||||
|
ARGS="-ip `hostname -i` -dir /data" |
||||
|
if [ -n "$MASTER_PORT_9333_TCP_ADDR" ] ; then |
||||
|
ARGS="$ARGS -master.peers=$MASTER_PORT_9333_TCP_ADDR:$MASTER_PORT_9333_TCP_PORT" |
||||
|
fi |
||||
|
exec /usr/bin/weed $@ $ARGS |
||||
|
;; |
||||
|
|
||||
|
*) |
||||
|
exec /usr/bin/weed $@ |
||||
|
;; |
||||
|
esac |
||||
@ -0,0 +1,67 @@ |
|||||
|
#MySQL filer mapping store |
||||
|
|
||||
|
## Schema format |
||||
|
|
||||
|
|
||||
|
Basically, uriPath and fid are the key elements stored in MySQL. In view of the optimization and user's usage, |
||||
|
adding primary key with integer type and involving createTime, updateTime, status fields should be somewhat meaningful. |
||||
|
Of course, you could customize the schema per your concretely circumstance freely. |
||||
|
|
||||
|
<pre><code> |
||||
|
CREATE TABLE IF NOT EXISTS `filer_mapping` ( |
||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT, |
||||
|
`uriPath` char(256) NOT NULL DEFAULT "" COMMENT 'http uriPath', |
||||
|
`fid` char(36) NOT NULL DEFAULT "" COMMENT 'seaweedfs fid', |
||||
|
`createTime` int(10) NOT NULL DEFAULT 0 COMMENT 'createdTime in unix timestamp', |
||||
|
`updateTime` int(10) NOT NULL DEFAULT 0 COMMENT 'updatedTime in unix timestamp', |
||||
|
`remark` varchar(20) NOT NULL DEFAULT "" COMMENT 'reserverd field', |
||||
|
`status` tinyint(2) DEFAULT '1' COMMENT 'resource status', |
||||
|
PRIMARY KEY (`id`), |
||||
|
UNIQUE KEY `index_uriPath` (`uriPath`) |
||||
|
) DEFAULT CHARSET=utf8; |
||||
|
</code></pre> |
||||
|
|
||||
|
|
||||
|
The MySQL 's config params is not added into the weed command option as other stores(redis,cassandra). Instead, |
||||
|
We created a config file(json format) for them. TOML,YAML or XML also should be OK. But TOML and YAML need import thirdparty package |
||||
|
while XML is a little bit complex. |
||||
|
|
||||
|
The sample config file's content is below: |
||||
|
|
||||
|
<pre><code> |
||||
|
{ |
||||
|
"mysql": [ |
||||
|
{ |
||||
|
"User": "root", |
||||
|
"Password": "root", |
||||
|
"HostName": "127.0.0.1", |
||||
|
"Port": 3306, |
||||
|
"DataBase": "seaweedfs" |
||||
|
}, |
||||
|
{ |
||||
|
"User": "root", |
||||
|
"Password": "root", |
||||
|
"HostName": "127.0.0.2", |
||||
|
"Port": 3306, |
||||
|
"DataBase": "seaweedfs" |
||||
|
} |
||||
|
], |
||||
|
"IsSharding":true, |
||||
|
"ShardCount":1024 |
||||
|
} |
||||
|
</code></pre> |
||||
|
|
||||
|
|
||||
|
The "mysql" field in above conf file is an array which include all mysql instances you prepared to store sharding data. |
||||
|
|
||||
|
1. If one mysql instance is enough, just keep one instance in "mysql" field. |
||||
|
|
||||
|
2. If table sharding at a specific mysql instance is needed , mark "IsSharding" field with true and specify total table sharding numbers using "ShardCount" field. |
||||
|
|
||||
|
3. If the mysql service could be auto scaled transparently in your environment, just config one mysql instance(usually it's a frondend proxy or VIP),and mark "IsSharding" with false value |
||||
|
|
||||
|
4. If you prepare more than one mysql instance and have no plan to use table sharding for any instance(mark isSharding with false), instance sharding will still be done implicitly |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,270 @@ |
|||||
|
package mysql_store |
||||
|
|
||||
|
import ( |
||||
|
"database/sql" |
||||
|
"fmt" |
||||
|
"hash/crc32" |
||||
|
"sync" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/chrislusf/seaweedfs/weed/filer" |
||||
|
|
||||
|
_ "github.com/go-sql-driver/mysql" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
sqlUrl = "%s:%s@tcp(%s:%d)/%s?charset=utf8" |
||||
|
default_maxIdleConnections = 100 |
||||
|
default_maxOpenConnections = 50 |
||||
|
default_maxTableNums = 1024 |
||||
|
tableName = "filer_mapping" |
||||
|
) |
||||
|
|
||||
|
var ( |
||||
|
_init_db sync.Once |
||||
|
_db_connections []*sql.DB |
||||
|
) |
||||
|
|
||||
|
type MySqlConf struct { |
||||
|
User string |
||||
|
Password string |
||||
|
HostName string |
||||
|
Port int |
||||
|
DataBase string |
||||
|
MaxIdleConnections int |
||||
|
MaxOpenConnections int |
||||
|
} |
||||
|
|
||||
|
type ShardingConf struct { |
||||
|
IsSharding bool `json:"isSharding"` |
||||
|
ShardCount int `json:"shardCount"` |
||||
|
} |
||||
|
|
||||
|
type MySqlStore struct { |
||||
|
dbs []*sql.DB |
||||
|
isSharding bool |
||||
|
shardCount int |
||||
|
} |
||||
|
|
||||
|
func getDbConnection(confs []MySqlConf) []*sql.DB { |
||||
|
_init_db.Do(func() { |
||||
|
for _, conf := range confs { |
||||
|
|
||||
|
sqlUrl := fmt.Sprintf(sqlUrl, conf.User, conf.Password, conf.HostName, conf.Port, conf.DataBase) |
||||
|
var dbErr error |
||||
|
_db_connection, dbErr := sql.Open("mysql", sqlUrl) |
||||
|
if dbErr != nil { |
||||
|
_db_connection.Close() |
||||
|
_db_connection = nil |
||||
|
panic(dbErr) |
||||
|
} |
||||
|
var maxIdleConnections, maxOpenConnections int |
||||
|
|
||||
|
if conf.MaxIdleConnections != 0 { |
||||
|
maxIdleConnections = conf.MaxIdleConnections |
||||
|
} else { |
||||
|
maxIdleConnections = default_maxIdleConnections |
||||
|
} |
||||
|
if conf.MaxOpenConnections != 0 { |
||||
|
maxOpenConnections = conf.MaxOpenConnections |
||||
|
} else { |
||||
|
maxOpenConnections = default_maxOpenConnections |
||||
|
} |
||||
|
|
||||
|
_db_connection.SetMaxIdleConns(maxIdleConnections) |
||||
|
_db_connection.SetMaxOpenConns(maxOpenConnections) |
||||
|
_db_connections = append(_db_connections, _db_connection) |
||||
|
} |
||||
|
}) |
||||
|
return _db_connections |
||||
|
} |
||||
|
|
||||
|
func NewMysqlStore(confs []MySqlConf, isSharding bool, shardCount int) *MySqlStore { |
||||
|
ms := &MySqlStore{ |
||||
|
dbs: getDbConnection(confs), |
||||
|
isSharding: isSharding, |
||||
|
shardCount: shardCount, |
||||
|
} |
||||
|
|
||||
|
for _, db := range ms.dbs { |
||||
|
if !isSharding { |
||||
|
ms.shardCount = 1 |
||||
|
} else { |
||||
|
if ms.shardCount == 0 { |
||||
|
ms.shardCount = default_maxTableNums |
||||
|
} |
||||
|
} |
||||
|
for i := 0; i < ms.shardCount; i++ { |
||||
|
if err := ms.createTables(db, tableName, i); err != nil { |
||||
|
fmt.Printf("create table failed %v", err) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ms |
||||
|
} |
||||
|
|
||||
|
func (s *MySqlStore) hash(fullFileName string) (instance_offset, table_postfix int) { |
||||
|
hash_value := crc32.ChecksumIEEE([]byte(fullFileName)) |
||||
|
instance_offset = int(hash_value) % len(s.dbs) |
||||
|
table_postfix = int(hash_value) % s.shardCount |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (s *MySqlStore) parseFilerMappingInfo(path string) (instanceId int, tableFullName string, err error) { |
||||
|
instance_offset, table_postfix := s.hash(path) |
||||
|
instanceId = instance_offset |
||||
|
if s.isSharding { |
||||
|
tableFullName = fmt.Sprintf("%s_%04d", tableName, table_postfix) |
||||
|
} else { |
||||
|
tableFullName = tableName |
||||
|
} |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (s *MySqlStore) Get(fullFilePath string) (fid string, err error) { |
||||
|
instance_offset, tableFullName, err := s.parseFilerMappingInfo(fullFilePath) |
||||
|
if err != nil { |
||||
|
return "", fmt.Errorf("MySqlStore Get operation can not parse file path %s: err is %v", fullFilePath, err) |
||||
|
} |
||||
|
fid, err = s.query(fullFilePath, s.dbs[instance_offset], tableFullName) |
||||
|
if err == sql.ErrNoRows { |
||||
|
//Could not found
|
||||
|
err = filer.ErrNotFound |
||||
|
} |
||||
|
return fid, err |
||||
|
} |
||||
|
|
||||
|
func (s *MySqlStore) Put(fullFilePath string, fid string) (err error) { |
||||
|
var tableFullName string |
||||
|
|
||||
|
instance_offset, tableFullName, err := s.parseFilerMappingInfo(fullFilePath) |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("MySqlStore Put operation can not parse file path %s: err is %v", fullFilePath, err) |
||||
|
} |
||||
|
var old_fid string |
||||
|
if old_fid, err = s.query(fullFilePath, s.dbs[instance_offset], tableFullName); err != nil && err != sql.ErrNoRows { |
||||
|
return fmt.Errorf("MySqlStore Put operation failed when querying path %s: err is %v", fullFilePath, err) |
||||
|
} else { |
||||
|
if len(old_fid) == 0 { |
||||
|
err = s.insert(fullFilePath, fid, s.dbs[instance_offset], tableFullName) |
||||
|
err = fmt.Errorf("MySqlStore Put operation failed when inserting path %s with fid %s : err is %v", fullFilePath, fid, err) |
||||
|
} else { |
||||
|
err = s.update(fullFilePath, fid, s.dbs[instance_offset], tableFullName) |
||||
|
err = fmt.Errorf("MySqlStore Put operation failed when updating path %s with fid %s : err is %v", fullFilePath, fid, err) |
||||
|
} |
||||
|
} |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (s *MySqlStore) Delete(fullFilePath string) (err error) { |
||||
|
var fid string |
||||
|
instance_offset, tableFullName, err := s.parseFilerMappingInfo(fullFilePath) |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("MySqlStore Delete operation can not parse file path %s: err is %v", fullFilePath, err) |
||||
|
} |
||||
|
if fid, err = s.query(fullFilePath, s.dbs[instance_offset], tableFullName); err != nil { |
||||
|
return fmt.Errorf("MySqlStore Delete operation failed when querying path %s: err is %v", fullFilePath, err) |
||||
|
} else if fid == "" { |
||||
|
return nil |
||||
|
} |
||||
|
if err = s.delete(fullFilePath, s.dbs[instance_offset], tableFullName); err != nil { |
||||
|
return fmt.Errorf("MySqlStore Delete operation failed when deleting path %s: err is %v", fullFilePath, err) |
||||
|
} else { |
||||
|
return nil |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (s *MySqlStore) Close() { |
||||
|
for _, db := range s.dbs { |
||||
|
db.Close() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var createTable = ` |
||||
|
CREATE TABLE IF NOT EXISTS %s ( |
||||
|
id bigint(20) NOT NULL AUTO_INCREMENT, |
||||
|
uriPath char(256) NOT NULL DEFAULT "" COMMENT 'http uriPath', |
||||
|
fid char(36) NOT NULL DEFAULT "" COMMENT 'seaweedfs fid', |
||||
|
createTime int(10) NOT NULL DEFAULT 0 COMMENT 'createdTime in unix timestamp', |
||||
|
updateTime int(10) NOT NULL DEFAULT 0 COMMENT 'updatedTime in unix timestamp', |
||||
|
remark varchar(20) NOT NULL DEFAULT "" COMMENT 'reserverd field', |
||||
|
status tinyint(2) DEFAULT '1' COMMENT 'resource status', |
||||
|
PRIMARY KEY (id), |
||||
|
UNIQUE KEY index_uriPath (uriPath) |
||||
|
) DEFAULT CHARSET=utf8; |
||||
|
` |
||||
|
|
||||
|
func (s *MySqlStore) createTables(db *sql.DB, tableName string, postfix int) error { |
||||
|
var realTableName string |
||||
|
if s.isSharding { |
||||
|
realTableName = fmt.Sprintf("%s_%4d", tableName, postfix) |
||||
|
} else { |
||||
|
realTableName = tableName |
||||
|
} |
||||
|
|
||||
|
stmt, err := db.Prepare(fmt.Sprintf(createTable, realTableName)) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
defer stmt.Close() |
||||
|
|
||||
|
_, err = stmt.Exec() |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (s *MySqlStore) query(uriPath string, db *sql.DB, tableName string) (string, error) { |
||||
|
sqlStatement := "SELECT fid FROM %s WHERE uriPath=?" |
||||
|
row := db.QueryRow(fmt.Sprintf(sqlStatement, tableName), uriPath) |
||||
|
var fid string |
||||
|
err := row.Scan(&fid) |
||||
|
if err != nil { |
||||
|
return "", err |
||||
|
} |
||||
|
return fid, nil |
||||
|
} |
||||
|
|
||||
|
func (s *MySqlStore) update(uriPath string, fid string, db *sql.DB, tableName string) error { |
||||
|
sqlStatement := "UPDATE %s SET fid=?, updateTime=? WHERE uriPath=?" |
||||
|
res, err := db.Exec(fmt.Sprintf(sqlStatement, tableName), fid, time.Now().Unix(), uriPath) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
_, err = res.RowsAffected() |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (s *MySqlStore) insert(uriPath string, fid string, db *sql.DB, tableName string) error { |
||||
|
sqlStatement := "INSERT INTO %s (uriPath,fid,createTime) VALUES(?,?,?)" |
||||
|
res, err := db.Exec(fmt.Sprintf(sqlStatement, tableName), uriPath, fid, time.Now().Unix()) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
_, err = res.RowsAffected() |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (s *MySqlStore) delete(uriPath string, db *sql.DB, tableName string) error { |
||||
|
sqlStatement := "DELETE FROM %s WHERE uriPath=?" |
||||
|
res, err := db.Exec(fmt.Sprintf(sqlStatement, tableName), uriPath) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
_, err = res.RowsAffected() |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
package mysql_store |
||||
|
|
||||
|
import ( |
||||
|
"encoding/json" |
||||
|
"hash/crc32" |
||||
|
"testing" |
||||
|
) |
||||
|
|
||||
|
func TestGenerateMysqlConf(t *testing.T) { |
||||
|
var conf []MySqlConf |
||||
|
conf = append(conf, MySqlConf{ |
||||
|
User: "root", |
||||
|
Password: "root", |
||||
|
HostName: "localhost", |
||||
|
Port: 3306, |
||||
|
DataBase: "seaweedfs", |
||||
|
}) |
||||
|
body, err := json.Marshal(conf) |
||||
|
if err != nil { |
||||
|
t.Errorf("json encoding err %s", err.Error()) |
||||
|
} |
||||
|
t.Logf("json output is %s", string(body)) |
||||
|
} |
||||
|
|
||||
|
func TestCRC32FullPathName(t *testing.T) { |
||||
|
fullPathName := "/prod-bucket/law632191483895612493300-signed.pdf" |
||||
|
hash_value := crc32.ChecksumIEEE([]byte(fullPathName)) |
||||
|
table_postfix := int(hash_value) % 1024 |
||||
|
t.Logf("table postfix %d", table_postfix) |
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
package storage |
||||
|
|
||||
|
import ( |
||||
|
"testing" |
||||
|
) |
||||
|
|
||||
|
/* |
||||
|
makediff test steps |
||||
|
1. launch weed server at your local/dev environment, (option |
||||
|
"garbageThreshold" for master and option "max" for volume should be set with specific value which would let |
||||
|
preparing test prerequisite easier ) |
||||
|
a) ./weed master -garbageThreshold=0.99 -mdir=./m |
||||
|
b) ./weed volume -dir=./data -max=1 -mserver=localhost:9333 -port=8080 |
||||
|
2. upload 4 different files, you could call dir/assign to get 4 different fids |
||||
|
a) upload file A with fid a |
||||
|
b) upload file B with fid b |
||||
|
c) upload file C with fid c |
||||
|
d) upload file D with fid d |
||||
|
3. update file A and C |
||||
|
a) modify file A and upload file A with fid a |
||||
|
b) modify file C and upload file C with fid c |
||||
|
c) record the current 1.idx's file size(lastCompactIndexOffset value) |
||||
|
4. Compacting the data file |
||||
|
a) run curl http://localhost:8080/admin/vacuum/compact?volumeId=1
|
||||
|
b) verify the 1.cpd and 1.cpx is created under volume directory |
||||
|
5. update file B and delete file D |
||||
|
a) modify file B and upload file B with fid b |
||||
|
d) delete file B with fid b |
||||
|
6. Now you could run the following UT case, the case should be run successfully |
||||
|
7. Compact commit manually |
||||
|
a) mv 1.cpd 1.dat |
||||
|
b) mv 1.cpx 1.idx |
||||
|
8. Restart Volume Server |
||||
|
9. Now you should get updated file A,B,C |
||||
|
*/ |
||||
|
|
||||
|
func TestMakeDiff(t *testing.T) { |
||||
|
|
||||
|
v := new(Volume) |
||||
|
//lastCompactIndexOffset value is the index file size before step 4
|
||||
|
v.lastCompactIndexOffset = 96 |
||||
|
v.SuperBlock.version = 0x2 |
||||
|
/* |
||||
|
err := v.makeupDiff( |
||||
|
"/yourpath/1.cpd", |
||||
|
"/yourpath/1.cpx", |
||||
|
"/yourpath/1.dat", |
||||
|
"/yourpath/1.idx") |
||||
|
if err != nil { |
||||
|
t.Errorf("makeupDiff err is %v", err) |
||||
|
} else { |
||||
|
t.Log("makeupDiff Succeeded") |
||||
|
} |
||||
|
*/ |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue