mirror of
https://github.com/haiwen/seafile-server.git
synced 2025-07-19 09:39:38 +00:00
* Initial commit for fileserver written in golang. [gofileserver] Fix some syntaxt errors. Add fs backend and objstore test (#352) * Add fs backend and objstore test * modify test case and optimize fs backend * Modify function name and first write temporary files * Don't need to reopen the temp files Add comment for objstore (#354) * Add comment for objstore * Modify comment Add commitmgr and test case (#356) * Add commitmgr and test case * Redefine the interface * Modify comment and interface * Modify parameter and del unused method * Add comment for FromData and ToData Add blockmgr and test case (#357) * Add blockmgr and test case * Modify comment and interface Add fsmgr and test case (#358) * Add fsmgr and test case * Add save interface and error details * Modify errors and comments Add searpc package and test case (#360) * Add searpc package * Add searpc test case * Add return error and add Request struct * Modify returned error * Modify comments add checkPerm (#369) Add file and block download (#363) * Add file and block download * Modify init and use aes algorithm * Get block by offset and add stat method * Modify objID's type * Fix reset pos after add start * Add http error handing and record log when failed to read block or write block to response * Modify http return code and value names * Modify http return code and add log info * Block read add comment and only repeat once load ccnetdb and support sqlite (#371) Add zip download (#372) * Add zip download * Modify pack dir and log info * Modify http return code and use Deflate zip compression methods add /repo/<repo-id>/permission-check (#375) add /<repo-id>/commit/HEAD (#377) add /repo/<repo-id>/commit/<id> (#379) add /repo/<repo-id>/block/<id> (#380) add /repo/<repo-id>/fs-id-list (#383) add /repo/head-commits-multi (#388) Add file upload api (#378) * Add file upload api * Upload api implements post multi files and create relative path * Modify handle error and save files directly * Fix rebase conflict * index block use channel and optimize mkdir with parents * Handle jobs and results in a loop * Mkdir with parents use postMultiFiles and use pointer of SeafDirent * Del diff_simple size_sched virtual_repo * Need to check the path with and without slash * Modify merge trees and add merge test case * Del postFile and don't close results channel * Close the file and remove multipart temp file * Modify merge test case and compare the first name of path * Use pointer of Entries for SeafDir * Add test cases for different situations add /repo/<repo-id>/pack-fs (#389) add POST /<repo-id>/check-fs and /<repo-id>/check-blocks (#396) Merge compute repo (#397) * Add update repo size and merge virtual repo * Eliminate lint warnings * Uncomment merge virtual repo and compute repo size * Need init the dents * Use interface{} param and modify removeElems * Move update dir to file.go and modify logs * Del sync pkg add PUT /<repo-id>/commit/<commit-id> (#400) add PUT /<repo-id>/block/<id> (#401) add POST /<repo-id>/recv-fs (#398) add PUT /<repo-id>/commit/HEAD (#402) Add http return code (#403) Add file update API (#399) * Add file update API * Add GetObjIDByPath and fix change size error * Add traffic statistics for update api add diffTrees unit test (#391) add GET /accessible-repos (#406) add GET /<repo-id>/block-map/<file-id> (#405) Add test update repo size and merge virtual repo (#409) * Update dir need update repo size * Add test update repo size and merge virtual repo * Add delay for test ajax * Add delay before get repo size and modify comment Use go fileserver for unit test (#410) * Use go fileserver for unit test * Blocking scheduling update repo size * Add delay because of sqlite doesn't support concurrency * Post use multipart form encode * Del mysql database when test finished * Fix merge virtual repo failed when use sqlite3 Add upload block API (#412) fixed error Add quota-check API (#426) use diff package * Use central conf for go fileserver (#428) * Use central conf for go fileserver * Fix log error * use store id and remove share get repo owner (#430) * Fix permission error (#432) Co-authored-by: feiniks <36756310+feiniks@users.noreply.github.com> Co-authored-by: Xiangyue Cai <caixiangyue007@gmail.com>
240 lines
5.2 KiB
Go
240 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"path/filepath"
|
|
|
|
"gopkg.in/ini.v1"
|
|
|
|
"database/sql"
|
|
|
|
"github.com/haiwen/seafile-server/fileserver/commitmgr"
|
|
"github.com/haiwen/seafile-server/fileserver/diff"
|
|
"github.com/haiwen/seafile-server/fileserver/fsmgr"
|
|
"github.com/haiwen/seafile-server/fileserver/repomgr"
|
|
)
|
|
|
|
// Job is the job object of workpool.
|
|
type Job struct {
|
|
callback jobCB
|
|
repoID string
|
|
}
|
|
|
|
type jobCB func(repoID string) error
|
|
|
|
var jobs = make(chan Job, 100)
|
|
|
|
func sizeSchedulerInit() {
|
|
var n int = 1
|
|
var seafileConfPath string
|
|
if centralDir != "" {
|
|
seafileConfPath = filepath.Join(centralDir, "seafile.conf")
|
|
} else {
|
|
seafileConfPath = filepath.Join(absDataDir, "seafile.conf")
|
|
}
|
|
config, err := ini.Load(seafileConfPath)
|
|
if err != nil {
|
|
log.Fatalf("Failed to load seafile.conf: %v", err)
|
|
}
|
|
if section, err := config.GetSection("scheduler"); err == nil {
|
|
if key, err := section.GetKey("size_sched_thread_num"); err == nil {
|
|
num, err := key.Int()
|
|
if err == nil {
|
|
n = num
|
|
}
|
|
}
|
|
}
|
|
go createWorkerPool(n)
|
|
}
|
|
|
|
// need to start a go routine
|
|
func createWorkerPool(n int) {
|
|
for i := 0; i < n; i++ {
|
|
go worker()
|
|
}
|
|
}
|
|
|
|
func worker() {
|
|
for {
|
|
select {
|
|
case job := <-jobs:
|
|
if job.callback != nil {
|
|
err := job.callback(job.repoID)
|
|
if err != nil {
|
|
log.Printf("failed to call jobs: %v.\n", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func updateRepoSize(repoID string) {
|
|
job := Job{computeRepoSize, repoID}
|
|
jobs <- job
|
|
}
|
|
|
|
func computeRepoSize(repoID string) error {
|
|
var size int64
|
|
var fileCount int64
|
|
|
|
repo := repomgr.Get(repoID)
|
|
if repo == nil {
|
|
err := fmt.Errorf("failed to get repo %s", repoID)
|
|
return err
|
|
}
|
|
|
|
info, err := getOldRepoInfo(repoID)
|
|
if err != nil {
|
|
err := fmt.Errorf("failed to get old repo info: %v", err)
|
|
return err
|
|
}
|
|
|
|
if info != nil && info.HeadID == repo.HeadCommitID {
|
|
return nil
|
|
}
|
|
|
|
head, err := commitmgr.Load(repo.ID, repo.HeadCommitID)
|
|
if err != nil {
|
|
err := fmt.Errorf("failed to get head commit %s", repo.HeadCommitID)
|
|
return err
|
|
}
|
|
|
|
var oldHead *commitmgr.Commit
|
|
if info != nil {
|
|
commit, _ := commitmgr.Load(repo.ID, info.HeadID)
|
|
oldHead = commit
|
|
}
|
|
|
|
if info != nil && oldHead != nil {
|
|
var results []*diff.DiffEntry
|
|
var changeSize int64
|
|
var changeFileCount int64
|
|
err := diff.DiffCommits(oldHead, head, &results, false)
|
|
if err != nil {
|
|
err := fmt.Errorf("failed to do diff commits: %v", err)
|
|
return err
|
|
}
|
|
|
|
for _, de := range results {
|
|
if de.Status == diff.DiffStatusDeleted {
|
|
changeSize -= de.Size
|
|
changeFileCount--
|
|
} else if de.Status == diff.DiffStatusAdded {
|
|
changeSize += de.Size
|
|
changeFileCount++
|
|
} else if de.Status == diff.DiffStatusModified {
|
|
changeSize = changeSize + de.Size - de.OriginSize
|
|
}
|
|
}
|
|
size = info.Size + changeSize
|
|
fileCount = info.FileCount + changeFileCount
|
|
} else {
|
|
info, err := fsmgr.GetFileCountInfoByPath(repo.StoreID, repo.RootID, "/")
|
|
if err != nil {
|
|
err := fmt.Errorf("failed to get file count")
|
|
return err
|
|
}
|
|
|
|
fileCount = info.FileCount
|
|
size = info.Size
|
|
}
|
|
|
|
err = setRepoSizeAndFileCount(repoID, repo.HeadCommitID, size, fileCount)
|
|
if err != nil {
|
|
err := fmt.Errorf("failed to set repo size and file count %s: %v", repoID, err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func setRepoSizeAndFileCount(repoID, newHeadID string, size, fileCount int64) error {
|
|
trans, err := seafileDB.Begin()
|
|
if err != nil {
|
|
err := fmt.Errorf("failed to start transaction: %v", err)
|
|
return err
|
|
}
|
|
|
|
var headID string
|
|
sqlStr := "SELECT head_id FROM RepoSize WHERE repo_id=?"
|
|
|
|
row := trans.QueryRow(sqlStr, repoID)
|
|
if err := row.Scan(&headID); err != nil {
|
|
if err != sql.ErrNoRows {
|
|
trans.Rollback()
|
|
return err
|
|
}
|
|
}
|
|
|
|
if headID == "" {
|
|
sqlStr := "INSERT INTO RepoSize (repo_id, size, head_id) VALUES (?, ?, ?)"
|
|
_, err = trans.Exec(sqlStr, repoID, size, newHeadID)
|
|
if err != nil {
|
|
trans.Rollback()
|
|
return err
|
|
}
|
|
} else {
|
|
sqlStr = "UPDATE RepoSize SET size = ?, head_id = ? WHERE repo_id = ?"
|
|
_, err = trans.Exec(sqlStr, size, newHeadID, repoID)
|
|
if err != nil {
|
|
trans.Rollback()
|
|
return err
|
|
}
|
|
}
|
|
|
|
var exist int
|
|
sqlStr = "SELECT 1 FROM RepoFileCount WHERE repo_id=?"
|
|
row = trans.QueryRow(sqlStr, repoID)
|
|
if err := row.Scan(&exist); err != nil {
|
|
if err != sql.ErrNoRows {
|
|
trans.Rollback()
|
|
return err
|
|
}
|
|
}
|
|
|
|
if exist != 0 {
|
|
sqlStr := "UPDATE RepoFileCount SET file_count=? WHERE repo_id=?"
|
|
_, err = trans.Exec(sqlStr, fileCount, repoID)
|
|
if err != nil {
|
|
trans.Rollback()
|
|
return err
|
|
}
|
|
} else {
|
|
sqlStr := "INSERT INTO RepoFileCount (repo_id,file_count) VALUES (?,?)"
|
|
_, err = trans.Exec(sqlStr, repoID, fileCount)
|
|
if err != nil {
|
|
trans.Rollback()
|
|
return err
|
|
}
|
|
}
|
|
|
|
trans.Commit()
|
|
|
|
return nil
|
|
}
|
|
|
|
// RepoInfo contains repo information.
|
|
type RepoInfo struct {
|
|
HeadID string
|
|
Size int64
|
|
FileCount int64
|
|
}
|
|
|
|
func getOldRepoInfo(repoID string) (*RepoInfo, error) {
|
|
sqlStr := "select s.head_id,s.size,f.file_count FROM RepoSize s LEFT JOIN RepoFileCount f ON " +
|
|
"s.repo_id=f.repo_id WHERE s.repo_id=?"
|
|
|
|
repoInfo := new(RepoInfo)
|
|
row := seafileDB.QueryRow(sqlStr, repoID)
|
|
if err := row.Scan(&repoInfo.HeadID, &repoInfo.Size, &repoInfo.FileCount); err != nil {
|
|
if err != sql.ErrNoRows {
|
|
return nil, err
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
return repoInfo, nil
|
|
}
|