Repository: dragonflyoss/Dragonfly2
Pull request #924 (2021-12-14 04:06:24)

refactor: scheduler register task
<!--- Provide a general summary of your changes in the Title above -->

## Description
- Refactor scheduler register task.
<!--- Describe your changes in detail -->

## Related Issue

<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->

## Motivation and Context

<!--- Why is this change required? What problem does it solve? -->

## Screenshots (if appropriate):

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation Update (if none of the other choices apply)

## Checklist:

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [x] Code compiles correctly.
- [x] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [x] I have read the **CONTRIBUTING** document.
- [x] I have added tests to cover my changes.
- [x] All new and existing tests passed.


codecov-commenter: # [Codecov](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/924?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) Report
> Merging [#924](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/924?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) (6a2af5b) into [main](https://codecov.io/gh/dragonflyoss/Dragonfly2/commit/869f3d18ed58e7dee35819af5f17795ce91e191d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) (869f3d1) will **increase** coverage by `0.02%`.
> The diff coverage is `54.54%`.

[![Impacted file tree graph](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/924/graphs/tree.svg?width=650&height=150&src=pr&token=PUICJYSAT7&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss)](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/924?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss)

```diff
@@            Coverage Diff             @@
##             main     #924      +/-   ##
==========================================
+ Coverage   43.07%   43.10%   +0.02%     
==========================================
  Files         106      106              
  Lines        8273     8272       -1     
==========================================
+ Hits         3564     3566       +2     
+ Misses       4249     4248       -1     
+ Partials      460      458       -2     
```

| Flag | Coverage Δ | |
|---|---|---|
| Object-compatibility-e2etests | `?` | |
| e2etests | `?` | |
| unittests | `43.10% <54.54%> (+0.02%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/924?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss) | Coverage Δ | |
|---|---|---|
| [scheduler/supervisor/task.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/924/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-c2NoZWR1bGVyL3N1cGVydmlzb3IvdGFzay5nbw==) | `67.15% <0.00%> (-2.55%)` | :arrow_down: |
| [internal/idgen/task\_id.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/924/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-aW50ZXJuYWwvaWRnZW4vdGFza19pZC5nbw==) | `100.00% <100.00%> (ø)` | |
| [pkg/util/net/urlutils/url\_utils.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/924/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-cGtnL3V0aWwvbmV0L3VybHV0aWxzL3VybF91dGlscy5nbw==) | `66.66% <100.00%> (+11.11%)` | :arrow_up: |
| [client/daemon/daemon.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/924/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-Y2xpZW50L2RhZW1vbi9kYWVtb24uZ28=) | `5.57% <0.00%> (-0.02%)` | :arrow_down: |
| [client/daemon/peer/peertask\_base.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/924/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-Y2xpZW50L2RhZW1vbi9wZWVyL3BlZXJ0YXNrX2Jhc2UuZ28=) | `46.69% <0.00%> (ø)` | |
| [pkg/source/source\_client.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/924/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-cGtnL3NvdXJjZS9zb3VyY2VfY2xpZW50Lmdv) | `1.88% <0.00%> (+0.01%)` | :arrow_up: |
| [cdn/supervisor/progress/progress.go](https://codecov.io/gh/dragonflyoss/Dragonfly2/pull/924/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=dragonflyoss#diff-Y2RuL3N1cGVydmlzb3IvcHJvZ3Jlc3MvcHJvZ3Jlc3MuZ28=) | `97.01% <0.00%> (+5.97%)` | :arrow_up: |


Patch:
diff --git a/cdn/cdn.go b/cdn/cdn.go
index a8b4dfc8..44854220 100644
--- a/cdn/cdn.go
+++ b/cdn/cdn.go
@@ -18,8 +18,6 @@ package cdn
 
 import (
 	"context"
-	"fmt"
-	"runtime"
 
 	"github.com/pkg/errors"
 	"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
@@ -29,7 +27,6 @@ import (
 	"d7y.io/dragonfly/v2/cdn/config"
 	"d7y.io/dragonfly/v2/cdn/gc"
 	"d7y.io/dragonfly/v2/cdn/metrics"
-	"d7y.io/dragonfly/v2/cdn/plugins"
 	"d7y.io/dragonfly/v2/cdn/rpcserver"
 	"d7y.io/dragonfly/v2/cdn/supervisor"
 	"d7y.io/dragonfly/v2/cdn/supervisor/cdn"
@@ -60,18 +57,9 @@ type Server struct {
 }
 
 // New creates a brand-new server instance.
-func New(cfg *config.Config) (*Server, error) {
-	if ok := storage.IsSupport(cfg.StorageMode); !ok {
-		return nil, fmt.Errorf("os %s is not support storage mode %s", runtime.GOOS, cfg.StorageMode)
-	}
-
-	// Initialize plugins
-	if err := plugins.Initialize(cfg.Plugins); err != nil {
-		return nil, errors.Wrapf(err, "init plugins")
-	}
-
+func New(config *config.Config) (*Server, error) {
 	// Initialize task manager
-	taskManager, err := task.NewManager(cfg)
+	taskManager, err := task.NewManager(config.Task)
 	if err != nil {
 		return nil, errors.Wrapf(err, "create task manager")
 	}
@@ -83,14 +71,13 @@ func New(cfg *config.Config) (*Server, error) {
 	}
 
 	// Initialize storage manager
-	storageManager, ok := storage.Get(cfg.StorageMode)
-	if !ok {
-		return nil, fmt.Errorf("can not find storage pattern %s", cfg.StorageMode)
+	storageManager, err := storage.NewManager(config.Storage, taskManager)
+	if err != nil {
+		return nil, errors.Wrapf(err, "create storage manager")
 	}
-	storageManager.Initialize(taskManager)
 
 	// Initialize CDN manager
-	cdnManager, err := cdn.NewManager(cfg, storageManager, progressManager, taskManager)
+	cdnManager, err := cdn.NewManager(config.CDN, storageManager, progressManager, taskManager)
 	if err != nil {
 		return nil, errors.Wrapf(err, "create cdn manager")
 	}
@@ -102,10 +89,10 @@ func New(cfg *config.Config) (*Server, error) {
 	}
 	// Initialize storage manager
 	var opts []grpc.ServerOption
-	if cfg.Options.Telemetry.Jaeger != "" {
+	if config.Options.Telemetry.Jaeger != "" {
 		opts = append(opts, grpc.ChainUnaryInterceptor(otelgrpc.UnaryServerInterceptor()), grpc.ChainStreamInterceptor(otelgrpc.StreamServerInterceptor()))
 	}
-	grpcServer, err := rpcserver.New(cfg, service, opts...)
+	grpcServer, err := rpcserver.New(config.RPCServer, service, opts...)
 	if err != nil {
 		return nil, errors.Wrap(err, "create rpcServer")
 	}
@@ -117,9 +104,9 @@ func New(cfg *config.Config) (*Server, error) {
 	}
 
 	var metricsServer *metrics.Server
-	if cfg.Metrics != nil && cfg.Metrics.Addr != "" {
+	if config.Metrics.Addr != "" {
 		// Initialize metrics server
-		metricsServer, err = metrics.New(cfg.Metrics, grpcServer.Server)
+		metricsServer, err = metrics.New(config.Metrics, grpcServer.Server)
 		if err != nil {
 			return nil, errors.Wrap(err, "create metricsServer")
 		}
@@ -127,14 +114,14 @@ func New(cfg *config.Config) (*Server, error) {
 
 	// Initialize configServer
 	var configServer managerClient.Client
-	if cfg.Manager.Addr != "" {
-		configServer, err = managerClient.New(cfg.Manager.Addr)
+	if config.Manager.Addr != "" {
+		configServer, err = managerClient.New(config.Manager.Addr)
 		if err != nil {
 			return nil, errors.Wrap(err, "create configServer")
 		}
 	}
 	return &Server{
-		config:        cfg,
+		config:        config,
 		grpcServer:    grpcServer,
 		metricsServer: metricsServer,
 		configServer:  configServer,
@@ -161,12 +148,13 @@ func (s *Server) Serve() error {
 
 	go func() {
 		if s.configServer != nil {
+			var rpcServerConfig = s.grpcServer.GetConfig()
 			CDNInstance, err := s.configServer.UpdateCDN(&manager.UpdateCDNRequest{
 				SourceType:   manager.SourceType_CDN_SOURCE,
 				HostName:     hostutils.FQDNHostname,
-				Ip:           s.config.AdvertiseIP,
-				Port:         int32(s.config.ListenPort),
-				DownloadPort: int32(s.config.DownloadPort),
+				Ip:           rpcServerConfig.AdvertiseIP,
+				Port:         int32(rpcServerConfig.ListenPort),
+				DownloadPort: int32(rpcServerConfig.DownloadPort),
 				Idc:          s.config.Host.IDC,
 				Location:     s.config.Host.Location,
 				CdnClusterId: uint64(s.config.Manager.CDNClusterID),
@@ -175,7 +163,7 @@ func (s *Server) Serve() error {
 				logger.Fatalf("update cdn instance failed: %v", err)
 			}
 			// Serve Keepalive
-			logger.Infof("====starting keepalive cdn instance %#v to manager %s====", CDNInstance)
+			logger.Infof("====starting keepalive cdn instance %s to manager %s====", CDNInstance, s.config.Manager.Addr)
 			s.configServer.KeepAlive(s.config.Manager.KeepAlive.Interval, &manager.KeepAliveRequest{
 				HostName:   hostutils.FQDNHostname,
 				SourceType: manager.SourceType_CDN_SOURCE,
diff --git a/cdn/config/config.go b/cdn/config/config.go
index 8c45c62f..d10f0caf 100644
--- a/cdn/config/config.go
+++ b/cdn/config/config.go
@@ -17,206 +17,76 @@
 package config
 
 import (
+	"fmt"
 	"time"
 
 	"gopkg.in/yaml.v3"
 
-	"d7y.io/dragonfly/v2/cdn/constants"
-	"d7y.io/dragonfly/v2/cdn/plugins"
-	"d7y.io/dragonfly/v2/cdn/storedriver"
+	"d7y.io/dragonfly/v2/cdn/metrics"
+	"d7y.io/dragonfly/v2/cdn/rpcserver"
+	"d7y.io/dragonfly/v2/cdn/supervisor/cdn"
+	"d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage"
+	"d7y.io/dragonfly/v2/cdn/supervisor/task"
 	"d7y.io/dragonfly/v2/cmd/dependency/base"
-	"d7y.io/dragonfly/v2/pkg/unit"
-	"d7y.io/dragonfly/v2/pkg/util/net/iputils"
 )
 
 // New creates an instant with default values.
 func New() *Config {
 	return &Config{
-		BaseProperties: NewDefaultBaseProperties(),
-		Plugins:        NewDefaultPlugins(),
-	}
-}
-
-// Config contains all configuration of cdn node.
-type Config struct {
-	base.Options    `yaml:",inline" mapstructure:",squash"`
-	*BaseProperties `yaml:"base" mapstructure:"base"`
-
-	Plugins map[plugins.PluginType][]*plugins.PluginProperties `yaml:"plugins" mapstructure:"plugins"`
-}
-
-func (c *Config) String() string {
-	if out, err := yaml.Marshal(c); err == nil {
-		return string(out)
-	}
-	return ""
-}
-
-// NewDefaultPlugins creates plugin instants with default values.
-func NewDefaultPlugins() map[plugins.PluginType][]*plugins.PluginProperties {
-	return map[plugins.PluginType][]*plugins.PluginProperties{
-		plugins.StorageDriverPlugin: {
-			{
-				Name:   "disk",
-				Enable: true,
-				Config: &storedriver.Config{
-					BaseDir: DefaultDiskBaseDir,
-				},
-			}, {
-				Name:   "memory",
-				Enable: false,
-				Config: &storedriver.Config{
-					BaseDir: DefaultMemoryBaseDir,
-				},
-			},
-		}, plugins.StorageManagerPlugin: {
-			{
-				Name:   "disk",
-				Enable: true,
-				Config: &StorageConfig{
-					GCInitialDelay: 0 * time.Second,
-					GCInterval:     15 * time.Second,
-					DriverConfigs: map[string]*DriverConfig{
-						"disk": {
-							GCConfig: &GCConfig{
-								YoungGCThreshold:  100 * unit.GB,
-								FullGCThreshold:   5 * unit.GB,
-								CleanRatio:        1,
-								IntervalThreshold: 2 * time.Hour,
-							}},
-					},
-				},
-			}, {
-				Name:   "hybrid",
-				Enable: false,
-				Config: &StorageConfig{
-					GCInitialDelay: 0 * time.Second,
-					GCInterval:     15 * time.Second,
-					DriverConfigs: map[string]*DriverConfig{
-						"disk": {
-							GCConfig: &GCConfig{
-								YoungGCThreshold:  100 * unit.GB,
-								FullGCThreshold:   5 * unit.GB,
-								CleanRatio:        1,
-								IntervalThreshold: 2 * time.Hour,
-							},
-						},
-						"memory": {
-							GCConfig: &GCConfig{
-								YoungGCThreshold:  100 * unit.GB,
-								FullGCThreshold:   5 * unit.GB,
-								CleanRatio:        3,
-								IntervalThreshold: 2 * time.Hour,
-							},
-						},
-					},
-				},
-			},
-		},
-	}
-}
-
-// NewDefaultBaseProperties creates an base properties instant with default values.
-func NewDefaultBaseProperties() *BaseProperties {
-	return &BaseProperties{
-		ListenPort:              constants.DefaultListenPort,
-		DownloadPort:            constants.DefaultDownloadPort,
-		SystemReservedBandwidth: constants.DefaultSystemReservedBandwidth,
-		MaxBandwidth:            constants.DefaultMaxBandwidth,
-		AdvertiseIP:             iputils.IPv4,
-		FailAccessInterval:      constants.DefaultFailAccessInterval,
-		GCInitialDelay:          constants.DefaultGCInitialDelay,
-		GCMetaInterval:          constants.DefaultGCMetaInterval,
-		TaskExpireTime:          constants.DefaultTaskExpireTime,
-		StorageMode:             constants.DefaultStorageMode,
+		Metrics:   metrics.DefaultConfig(),
+		Storage:   storage.DefaultConfig(),
+		RPCServer: rpcserver.DefaultConfig(),
+		Task:      task.DefaultConfig(),
+		CDN:       cdn.DefaultConfig(),
 		Manager: ManagerConfig{
+			Addr:         "",
+			CDNClusterID: 0,
 			KeepAlive: KeepAliveConfig{
-				Interval: constants.DefaultKeepAliveInterval,
+				Interval: 5 * time.Second,
 			},
 		},
-		Host: HostConfig{},
-		Metrics: &RestConfig{
-			Addr: ":8080",
+		Host: HostConfig{
+			Location: "",
+			IDC:      "",
 		},
+		LogDir: "",
 	}
 }
 
-type StorageConfig struct {
-	GCInitialDelay time.Duration            `yaml:"gcInitialDelay"`
-	GCInterval     time.Duration            `yaml:"gcInterval"`
-	DriverConfigs  map[string]*DriverConfig `yaml:"driverConfigs"`
-}
-
-type DriverConfig struct {
-	GCConfig *GCConfig `yaml:"gcConfig"`
-}
-
-// GCConfig gc config
-type GCConfig struct {
-	YoungGCThreshold  unit.Bytes    `yaml:"youngGCThreshold"`
-	FullGCThreshold   unit.Bytes    `yaml:"fullGCThreshold"`
-	CleanRatio        int           `yaml:"cleanRatio"`
-	IntervalThreshold time.Duration `yaml:"intervalThreshold"`
-}
-
-// BaseProperties contains all basic properties of cdn system.
-type BaseProperties struct {
-	// ListenPort is the port cdn server listens on.
-	// default: 8002
-	ListenPort int `yaml:"listenPort" mapstructure:"listenPort"`
-
-	// DownloadPort is the port for download files from cdn.
-	// default: 8001
-	DownloadPort int `yaml:"downloadPort" mapstructure:"downloadPort"`
-
-	// SystemReservedBandwidth is the network bandwidth reserved for system software.
-	// default: 20 MB, in format of G(B)/g/M(B)/m/K(B)/k/B, pure number will also be parsed as Byte.
-	SystemReservedBandwidth unit.Bytes `yaml:"systemReservedBandwidth" mapstructure:"systemReservedBandwidth"`
-
-	// MaxBandwidth is the network bandwidth that cdn system can use.
-	// default: 200 MB, in format of G(B)/g/M(B)/m/K(B)/k/B, pure number will also be parsed as Byte.
-	MaxBandwidth unit.Bytes `yaml:"maxBandwidth" mapstructure:"maxBandwidth"`
-
-	// AdvertiseIP is used to set the ip that we advertise to other peer in the p2p-network.
-	// By default, the first non-loop address is advertised.
-	AdvertiseIP string `yaml:"advertiseIP" mapstructure:"advertiseIP"`
-
-	// FailAccessInterval is the interval time after failed to access the URL.
-	// unit: minutes
-	// default: 3
-	FailAccessInterval time.Duration `yaml:"failAccessInterval" mapstructure:"failAccessInterval"`
-
-	// gc related
-	// GCInitialDelay is the delay time from the start to the first GC execution.
-	// default: 6s
-	GCInitialDelay time.Duration `yaml:"gcInitialDelay" mapstructure:"gcInitialDelay"`
-
-	// GCMetaInterval is the interval time to execute GC meta.
-	// default: 2min
-	GCMetaInterval time.Duration `yaml:"gcMetaInterval" mapstructure:"gcMetaInterval"`
-
-	// TaskExpireTime when a task is not accessed within the taskExpireTime,
-	// and it will be treated to be expired.
-	// default: 3min
-	TaskExpireTime time.Duration `yaml:"taskExpireTime" mapstructure:"taskExpireTime"`
-
-	// StorageMode disk/hybrid/memory
-	StorageMode string `yaml:"storageMode" mapstructure:"storageMode"`
-
+// Config contains all configuration of cdn node.
+type Config struct {
+	base.Options `yaml:",inline" mapstructure:",squash"`
+	Metrics      metrics.Config   `yaml:"metrics" mapstructure:"metrics"`
+	Storage      storage.Config   `yaml:"storage" mapstructure:"storage"`
+	RPCServer    rpcserver.Config `yaml:"rpcServer" mapstructure:"rpcServer"`
+	Task         task.Config      `yaml:"taskConfig" mapstructure:"taskConfig"`
+	CDN          cdn.Config       `yaml:"cdnConfig" mapstructure:"cdnConfig"`
+	// Manager configuration
+	Manager ManagerConfig `yaml:"manager" mapstructure:"manager"`
+	// Host configuration
+	Host HostConfig `yaml:"host" mapstructure:"host"`
 	// Log directory
 	LogDir string `yaml:"logDir" mapstructure:"logDir"`
-
 	// WorkHome directory
 	WorkHome string `mapstructure:"workHome" yaml:"workHome"`
+}
 
-	// Manager configuration
-	Manager ManagerConfig `yaml:"manager" mapstructure:"manager"`
-
-	// Host configuration
-	Host HostConfig `yaml:"host" mapstructure:"host"`
+func (c *Config) String() string {
+	if out, err := yaml.Marshal(c); err == nil {
+		return string(out)
+	}
+	return ""
+}
 
-	// Metrics configuration
-	Metrics *RestConfig `yaml:"metrics" mapstructure:"metrics"`
+func (c *Config) Validate() []error {
+	var errs []error
+	errs = append(errs, c.Metrics.Validate()...)
+	errs = append(errs, c.Storage.Validate()...)
+	errs = append(errs, c.RPCServer.Validate()...)
+	errs = append(errs, c.Task.Validate()...)
+	errs = append(errs, c.CDN.Validate()...)
+	errs = append(errs, c.Manager.Validate()...)
+	return errs
 }
 
 type ManagerConfig struct {
@@ -230,11 +100,30 @@ type ManagerConfig struct {
 	KeepAlive KeepAliveConfig `yaml:"keepAlive" mapstructure:"keepAlive"`
 }
 
+func (c ManagerConfig) Validate() []error {
+	var errors []error
+	if c.Addr != "" {
+		if c.CDNClusterID <= 0 {
+			errors = append(errors, fmt.Errorf("cdn cluster id %d can't be a negative number", c.CDNClusterID))
+		}
+		errors = append(errors, c.KeepAlive.Validate()...)
+	}
+	return errors
+}
+
 type KeepAliveConfig struct {
 	// Keep alive interval
 	Interval time.Duration `yaml:"interval" mapstructure:"interval"`
 }
 
+func (c KeepAliveConfig) Validate() []error {
+	var errors []error
+	if c.Interval <= 0 {
+		errors = append(errors, fmt.Errorf("keep alive interval %d can't be a negative number", c.Interval))
+	}
+	return errors
+}
+
 type HostConfig struct {
 	// Location for scheduler
 	Location string `mapstructure:"location" yaml:"location"`
@@ -242,7 +131,3 @@ type HostConfig struct {
 	// IDC for scheduler
 	IDC string `mapstructure:"idc" yaml:"idc"`
 }
-
-type RestConfig struct {
-	Addr string `yaml:"addr" mapstructure:"addr"`
-}
diff --git a/cdn/config/old_config.go b/cdn/config/old_config.go
new file mode 100644
index 00000000..0a04c066
--- /dev/null
+++ b/cdn/config/old_config.go
@@ -0,0 +1,320 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package config
+
+import (
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"time"
+
+	"github.com/mitchellh/mapstructure"
+	"gopkg.in/yaml.v3"
+
+	"d7y.io/dragonfly/v2/cdn/metrics"
+	"d7y.io/dragonfly/v2/cdn/plugins"
+	"d7y.io/dragonfly/v2/cdn/rpcserver"
+	"d7y.io/dragonfly/v2/cdn/storedriver"
+	"d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage"
+	_ "d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage/disk"   //nolint:gci    // Register disk storage manager
+	_ "d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage/hybrid" // Register hybrid storage manager
+	"d7y.io/dragonfly/v2/cdn/supervisor/task"
+	"d7y.io/dragonfly/v2/cmd/dependency/base"
+	"d7y.io/dragonfly/v2/pkg/basic"
+	"d7y.io/dragonfly/v2/pkg/unit"
+	"d7y.io/dragonfly/v2/pkg/util/net/iputils"
+)
+
+// NewDeprecatedConfig creates an instant with default values.
+func NewDeprecatedConfig() *DeprecatedConfig {
+	return &DeprecatedConfig{
+		BaseProperties: NewDefaultBaseProperties(),
+		Plugins:        NewDefaultPlugins(),
+	}
+}
+
+// DeprecatedConfig contains all configuration of cdn node.
+type DeprecatedConfig struct {
+	base.Options    `yaml:",inline" mapstructure:",squash"`
+	*BaseProperties `yaml:"base" mapstructure:"base"`
+
+	Plugins map[plugins.PluginType][]*plugins.PluginProperties `yaml:"plugins" mapstructure:"plugins"`
+}
+
+func (c DeprecatedConfig) Convert() *Config {
+	newConfig := New()
+	baseProperties := c.BaseProperties
+	newConfig.Manager = baseProperties.Manager
+	newConfig.Host = baseProperties.Host
+	newConfig.LogDir = baseProperties.LogDir
+	newConfig.WorkHome = baseProperties.WorkHome
+	if baseProperties.Metrics != nil {
+		newConfig.Metrics = metrics.Config{
+			Net:  "tcp",
+			Addr: baseProperties.Metrics.Addr,
+		}
+	}
+	newConfig.Task = task.Config{
+		GCInitialDelay:     baseProperties.GCInitialDelay,
+		GCMetaInterval:     baseProperties.GCMetaInterval,
+		ExpireTime:         baseProperties.TaskExpireTime,
+		FailAccessInterval: baseProperties.FailAccessInterval,
+	}
+	newConfig.CDN.SystemReservedBandwidth = baseProperties.SystemReservedBandwidth
+	newConfig.CDN.MaxBandwidth = baseProperties.MaxBandwidth
+	newConfig.RPCServer = rpcserver.Config{
+		AdvertiseIP:  baseProperties.AdvertiseIP,
+		ListenPort:   baseProperties.ListenPort,
+		DownloadPort: baseProperties.DownloadPort,
+	}
+	for _, properties := range c.Plugins[plugins.StorageManagerPlugin] {
+		if properties.Enable == false {
+			continue
+		}
+		storageManager := properties
+		newConfig.Storage.StorageMode = storageManager.Name
+		storageConfig := &StorageConfig{}
+		if err := decodeStorageManager(storageManager.Config, storageConfig); err == nil {
+			newConfig.Storage.GCInitialDelay = storageConfig.GCInitialDelay
+			newConfig.Storage.GCInterval = storageConfig.GCInterval
+			for driverName, config := range storageConfig.DriverConfigs {
+				var baseDir string
+				for _, pluginProperties := range c.Plugins[plugins.StorageDriverPlugin] {
+					if pluginProperties.Name != driverName || pluginProperties.Enable == false {
+						continue
+					}
+					driverConfig := &storedriver.Config{}
+					if err := mapstructure.Decode(pluginProperties.Config, driverConfig); err == nil {
+						baseDir = driverConfig.BaseDir
+					}
+				}
+
+				newConfig.Storage.DriverConfigs[driverName] = &storage.DriverConfig{
+					BaseDir: baseDir,
+					DriverGCConfig: &storage.DriverGCConfig{
+						YoungGCThreshold:  config.GCConfig.YoungGCThreshold,
+						FullGCThreshold:   config.GCConfig.FullGCThreshold,
+						CleanRatio:        config.GCConfig.CleanRatio,
+						IntervalThreshold: config.GCConfig.IntervalThreshold,
+					},
+				}
+				newConfig.Storage.DriverConfigs[driverName].DriverGCConfig.CleanRatio = config.GCConfig.CleanRatio
+			}
+		}
+	}
+	newConfig.Verbose = c.Verbose
+	newConfig.Console = c.Console
+	newConfig.Telemetry = c.Telemetry
+	newConfig.PProfPort = c.PProfPort
+	return newConfig
+}
+
+func decodeStorageManager(conf interface{}, sc *StorageConfig) error {
+	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
+		DecodeHook: mapstructure.ComposeDecodeHookFunc(func(from, to reflect.Type, v interface{}) (interface{}, error) {
+			switch to {
+			case reflect.TypeOf(unit.B),
+				reflect.TypeOf(time.Second):
+				b, _ := yaml.Marshal(v)
+				p := reflect.New(to)
+				if err := yaml.Unmarshal(b, p.Interface()); err != nil {
+					return nil, err
+				}
+				return p.Interface(), nil
+			default:
+				return v, nil
+			}
+		}),
+		Result: sc,
+	})
+	if err != nil {
+		return fmt.Errorf("parse config: %v", err)
+	}
+	return decoder.Decode(conf)
+}
+
+// NewDefaultPlugins creates plugin instants with default values.
+func NewDefaultPlugins() map[plugins.PluginType][]*plugins.PluginProperties {
+	return map[plugins.PluginType][]*plugins.PluginProperties{
+		plugins.StorageDriverPlugin: {
+			{
+				Name:   "disk",
+				Enable: true,
+				Config: &storedriver.Config{
+					BaseDir: filepath.Join(basic.HomeDir, "ftp"),
+				},
+			}, {
+				Name:   "memory",
+				Enable: false,
+				Config: &storedriver.Config{
+					BaseDir: "/dev/shm/dragonfly",
+				},
+			},
+		}, plugins.StorageManagerPlugin: {
+			{
+				Name:   "disk",
+				Enable: true,
+				Config: &StorageConfig{
+					GCInitialDelay: 0 * time.Second,
+					GCInterval:     15 * time.Second,
+					DriverConfigs: map[string]*DriverConfig{
+						"disk": {
+							GCConfig: &GCConfig{
+								YoungGCThreshold:  100 * unit.GB,
+								FullGCThreshold:   5 * unit.GB,
+								CleanRatio:        1,
+								IntervalThreshold: 2 * time.Hour,
+							},
+						},
+					},
+				},
+			}, {
+				Name:   "hybrid",
+				Enable: false,
+				Config: &StorageConfig{
+					GCInitialDelay: 0 * time.Second,
+					GCInterval:     15 * time.Second,
+					DriverConfigs: map[string]*DriverConfig{
+						"disk": {
+							GCConfig: &GCConfig{
+								YoungGCThreshold:  100 * unit.GB,
+								FullGCThreshold:   5 * unit.GB,
+								CleanRatio:        1,
+								IntervalThreshold: 2 * time.Hour,
+							},
+						},
+						"memory": {
+							GCConfig: &GCConfig{
+								YoungGCThreshold:  100 * unit.GB,
+								FullGCThreshold:   5 * unit.GB,
+								CleanRatio:        3,
+								IntervalThreshold: 2 * time.Hour,
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+}
+
+// NewDefaultBaseProperties creates an base properties instant with default values.
+func NewDefaultBaseProperties() *BaseProperties {
+	return &BaseProperties{
+		ListenPort:              8003,
+		DownloadPort:            8001,
+		SystemReservedBandwidth: 20 * unit.MB,
+		MaxBandwidth:            1 * unit.GB,
+		AdvertiseIP:             iputils.IPv4,
+		FailAccessInterval:      3 * time.Minute,
+		GCInitialDelay:          6 * time.Second,
+		GCMetaInterval:          2 * time.Minute,
+		TaskExpireTime:          3 * time.Minute,
+		StorageMode:             "disk",
+		Manager: ManagerConfig{
+			KeepAlive: KeepAliveConfig{
+				Interval: 5 * time.Second,
+			},
+		},
+		Host: HostConfig{},
+		Metrics: &RestConfig{
+			Addr: ":8080",
+		},
+	}
+}
+
+type StorageConfig struct {
+	GCInitialDelay time.Duration            `yaml:"gcInitialDelay"`
+	GCInterval     time.Duration            `yaml:"gcInterval"`
+	DriverConfigs  map[string]*DriverConfig `yaml:"driverConfigs"`
+}
+
+type DriverConfig struct {
+	GCConfig *GCConfig `yaml:"gcConfig"`
+}
+
+// GCConfig gc config
+type GCConfig struct {
+	YoungGCThreshold  unit.Bytes    `yaml:"youngGCThreshold"`
+	FullGCThreshold   unit.Bytes    `yaml:"fullGCThreshold"`
+	CleanRatio        int           `yaml:"cleanRatio"`
+	IntervalThreshold time.Duration `yaml:"intervalThreshold"`
+}
+
+// BaseProperties contains all basic properties of cdn system.
+type BaseProperties struct {
+	// ListenPort is the port cdn server listens on.
+	// default: 8002
+	ListenPort int `yaml:"listenPort" mapstructure:"listenPort"`
+
+	// DownloadPort is the port for download files from cdn.
+	// default: 8001
+	DownloadPort int `yaml:"downloadPort" mapstructure:"downloadPort"`
+
+	// SystemReservedBandwidth is the network bandwidth reserved for system software.
+	// default: 20 MB, in format of G(B)/g/M(B)/m/K(B)/k/B, pure number will also be parsed as Byte.
+	SystemReservedBandwidth unit.Bytes `yaml:"systemReservedBandwidth" mapstructure:"systemReservedBandwidth"`
+
+	// MaxBandwidth is the network bandwidth that cdn system can use.
+	// default: 200 MB, in format of G(B)/g/M(B)/m/K(B)/k/B, pure number will also be parsed as Byte.
+	MaxBandwidth unit.Bytes `yaml:"maxBandwidth" mapstructure:"maxBandwidth"`
+
+	// AdvertiseIP is used to set the ip that we advertise to other peer in the p2p-network.
+	// By default, the first non-loop address is advertised.
+	AdvertiseIP string `yaml:"advertiseIP" mapstructure:"advertiseIP"`
+
+	// FailAccessInterval is the interval time after failed to access the URL.
+	// unit: minutes
+	// default: 3
+	FailAccessInterval time.Duration `yaml:"failAccessInterval" mapstructure:"failAccessInterval"`
+
+	// gc related
+	// GCInitialDelay is the delay time from the start to the first GC execution.
+	// default: 6s
+	GCInitialDelay time.Duration `yaml:"gcInitialDelay" mapstructure:"gcInitialDelay"`
+
+	// GCMetaInterval is the interval time to execute GC meta.
+	// default: 2min
+	GCMetaInterval time.Duration `yaml:"gcMetaInterval" mapstructure:"gcMetaInterval"`
+
+	// TaskExpireTime when a task is not accessed within the taskExpireTime,
+	// and it will be treated to be expired.
+	// default: 3min
+	TaskExpireTime time.Duration `yaml:"taskExpireTime" mapstructure:"taskExpireTime"`
+
+	// StorageMode disk/hybrid/memory
+	StorageMode string `yaml:"storageMode" mapstructure:"storageMode"`
+
+	// Log directory
+	LogDir string `yaml:"logDir" mapstructure:"logDir"`
+
+	// WorkHome directory
+	WorkHome string `mapstructure:"workHome" yaml:"workHome"`
+
+	// Manager configuration
+	Manager ManagerConfig `yaml:"manager" mapstructure:"manager"`
+
+	// Host configuration
+	Host HostConfig `yaml:"host" mapstructure:"host"`
+
+	// Metrics configuration
+	Metrics *RestConfig `yaml:"metrics" mapstructure:"metrics"`
+}
+
+type RestConfig struct {
+	Addr string `yaml:"addr" mapstructure:"addr"`
+}
diff --git a/cdn/constants/constants.go b/cdn/constants/constants.go
deleted file mode 100644
index d5bd293e..00000000
--- a/cdn/constants/constants.go
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *     Copyright 2020 The Dragonfly Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package constants
-
-import (
-	"time"
-
-	"d7y.io/dragonfly/v2/pkg/unit"
-)
-
-const (
-	CDNWriterRoutineLimit = 4
-)
-
-const (
-	// DefaultListenPort is the default port cdn server listens on.
-	DefaultListenPort = 8003
-	// DefaultDownloadPort is the default port for download files from cdn.
-	DefaultDownloadPort = 8001
-)
-
-const (
-	DefaultStorageMode = "disk"
-)
-
-const (
-	// DefaultFailAccessInterval is the interval time after failed to access the URL.
-	DefaultFailAccessInterval = 3 * time.Minute
-)
-
-// gc
-const (
-	// DefaultGCInitialDelay is the delay time from the start to the first GC execution.
-	DefaultGCInitialDelay = 6 * time.Second
-
-	// DefaultGCMetaInterval is the interval time to execute the GC meta.
-	DefaultGCMetaInterval = 2 * time.Minute
-
-	// DefaultTaskExpireTime when a task is not accessed within the taskExpireTime,
-	// and it will be treated to be expired.
-	DefaultTaskExpireTime = 30 * time.Minute
-)
-
-const (
-	// DefaultSystemReservedBandwidth is the default network bandwidth reserved for system software.
-	// unit: MB/s
-	DefaultSystemReservedBandwidth = 20 * unit.MB
-	// DefaultMaxBandwidth is the default network bandwidth that cdn can use.
-	// unit: MB/s
-	DefaultMaxBandwidth = 1 * unit.GB
-)
-
-const (
-	DefaultKeepAliveInterval = 5 * time.Second
-)
diff --git a/cdn/metrics/config.go b/cdn/metrics/config.go
new file mode 100644
index 00000000..9a0ca48d
--- /dev/null
+++ b/cdn/metrics/config.go
@@ -0,0 +1,62 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package metrics
+
+import (
+	"fmt"
+	"net"
+)
+
+const DefaultNetwork = "tcp"
+
+type Config struct {
+	Net  string `yaml:"net" mapstructure:"net"`
+	Addr string `yaml:"addr" mapstructure:"addr"`
+}
+
+func DefaultConfig() Config {
+	config := Config{}
+	return config.applyDefaults()
+}
+
+func (c Config) applyDefaults() Config {
+	if c.Net == "" {
+		c.Net = DefaultNetwork
+	}
+	if c.Addr == "" {
+		c.Addr = ":8080"
+	}
+	return c
+}
+
+func (c Config) Validate() []error {
+	var errors []error
+	if c.Net != "" && c.Net != DefaultNetwork && c.Net != "unix" {
+		errors = append(errors, fmt.Errorf("metrics net only support tcp and unix, but is: %s", c.Net))
+	}
+	if c.Net == DefaultNetwork {
+		if _, err := net.ResolveTCPAddr(c.Net, c.Addr); err != nil {
+			errors = append(errors, fmt.Errorf("metrics addr is configured incorrect: %v", err))
+		}
+	}
+	if c.Net == "unix" {
+		if _, err := net.ResolveUnixAddr(c.Net, c.Addr); err != nil {
+			errors = append(errors, fmt.Errorf("metrics addr is configured incorrect: %v", err))
+		}
+	}
+	return errors
+}
diff --git a/cdn/config/store_path.go b/cdn/metrics/config_test.go
similarity index 54%
rename from cdn/config/store_path.go
rename to cdn/metrics/config_test.go
index 83160a1e..60216283 100644
--- a/cdn/config/store_path.go
+++ b/cdn/metrics/config_test.go
@@ -14,16 +14,28 @@
  * limitations under the License.
  */
 
-package config
+package metrics
 
 import (
-	"path/filepath"
+	"net"
+	"testing"
 
-	"d7y.io/dragonfly/v2/pkg/basic"
+	"github.com/stretchr/testify/assert"
 )
 
-var (
-	DefaultDiskBaseDir = filepath.Join(basic.HomeDir, "ftp")
-
-	DefaultMemoryBaseDir = "/dev/shm/dragonfly"
-)
+func TestConfig(t *testing.T) {
+	tcpConfig := Config{
+		Net:  "tcp",
+		Addr: "1.1.1.1:22",
+	}
+	addr, err := net.ResolveTCPAddr(tcpConfig.Net, tcpConfig.Addr)
+	assert.Equal(t, tcpConfig.Addr, addr.String())
+	assert.Nil(t, err)
+	unixConfig := Config{
+		Net:  "unix",
+		Addr: "/unix/metrics.sock",
+	}
+	unixAddr, err := net.ResolveUnixAddr(unixConfig.Net, unixConfig.Addr)
+	assert.Equal(t, unixConfig.Addr, unixAddr.String())
+	assert.Nil(t, err)
+}
diff --git a/cdn/metrics/metrics.go b/cdn/metrics/metrics.go
index 9f3d12c5..339b6b96 100644
--- a/cdn/metrics/metrics.go
+++ b/cdn/metrics/metrics.go
@@ -27,9 +27,7 @@ import (
 	"github.com/prometheus/client_golang/prometheus/promauto"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"google.golang.org/grpc"
-	"gopkg.in/yaml.v3"
 
-	"d7y.io/dragonfly/v2/cdn/config"
 	"d7y.io/dragonfly/v2/internal/constants"
 	logger "d7y.io/dragonfly/v2/internal/dflog"
 )
@@ -66,17 +64,11 @@ var (
 )
 
 type Server struct {
-	config     *config.RestConfig
+	config     Config
 	httpServer *http.Server
 }
 
-func New(config *config.RestConfig, rpcServer *grpc.Server) (*Server, error) {
-	// scheduler config values
-	s, err := yaml.Marshal(config)
-	if err != nil {
-		return nil, errors.Wrap(err, "marshal metrics server config")
-	}
-	logger.Infof("metrics server config: \n%s", s)
+func New(config Config, rpcServer *grpc.Server) (*Server, error) {
 	grpc_prometheus.Register(rpcServer)
 
 	return &Server{
@@ -94,7 +86,7 @@ func (s *Server) Handler() http.Handler {
 
 // ListenAndServe is a blocking call which runs s.
 func (s *Server) ListenAndServe(h http.Handler) error {
-	l, err := net.Listen("tcp", s.config.Addr)
+	l, err := net.Listen(s.config.Net, s.config.Addr)
 	if err != nil {
 		return err
 	}
diff --git a/cdn/rpcserver/config.go b/cdn/rpcserver/config.go
new file mode 100644
index 00000000..4abd5c62
--- /dev/null
+++ b/cdn/rpcserver/config.go
@@ -0,0 +1,77 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package rpcserver
+
+import (
+	"fmt"
+
+	"d7y.io/dragonfly/v2/pkg/util/net/iputils"
+)
+
+type Config struct {
+
+	// AdvertiseIP is used to set the ip that we advertise to other peer in the p2p-network.
+	// By default, the first non-loop address is advertised.
+	AdvertiseIP string `yaml:"advertiseIP" mapstructure:"advertiseIP"`
+
+	// ListenPort is the port cdn server listens on.
+	// default: 8002
+	ListenPort int `yaml:"listenPort" mapstructure:"listenPort"`
+
+	// DownloadPort is the port for download files from cdn.
+	// default: 8001
+	DownloadPort int `yaml:"downloadPort" mapstructure:"downloadPort"`
+}
+
+func DefaultConfig() Config {
+	config := Config{}
+	return config.applyDefaults()
+}
+
+func (c Config) applyDefaults() Config {
+	if c.AdvertiseIP == "" {
+		c.AdvertiseIP = iputils.IPv4
+	}
+	if c.ListenPort == 0 {
+		c.ListenPort = DefaultListenPort
+	}
+	if c.DownloadPort == 0 {
+		c.DownloadPort = DefaultDownloadPort
+	}
+	return c
+}
+
+func (c Config) Validate() []error {
+	var errors []error
+	if c.AdvertiseIP == "" {
+		errors = append(errors, fmt.Errorf("rpc server AdvertiseIP can't be empty"))
+	}
+	if c.ListenPort > 65535 || c.ListenPort < 1024 {
+		errors = append(errors, fmt.Errorf("rpc server ListenPort must be between 0 and 65535, inclusive. but is: %d", c.ListenPort))
+	}
+	if c.DownloadPort > 65535 || c.DownloadPort < 1024 {
+		errors = append(errors, fmt.Errorf("rpc server DownloadPort must be between 0 and 65535, inclusive. but is: %d", c.DownloadPort))
+	}
+	return errors
+}
+
+const (
+	// DefaultListenPort is the default port cdn server listens on.
+	DefaultListenPort = 8003
+	// DefaultDownloadPort is the default port for download files from cdn.
+	DefaultDownloadPort = 8001
+)
diff --git a/cdn/rpcserver/rpcserver.go b/cdn/rpcserver/rpcserver.go
index 8247692b..2eef1db7 100644
--- a/cdn/rpcserver/rpcserver.go
+++ b/cdn/rpcserver/rpcserver.go
@@ -27,7 +27,6 @@ import (
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/peer"
 
-	"d7y.io/dragonfly/v2/cdn/config"
 	"d7y.io/dragonfly/v2/cdn/constants"
 	"d7y.io/dragonfly/v2/cdn/supervisor"
 	"d7y.io/dragonfly/v2/cdn/supervisor/task"
@@ -46,12 +45,12 @@ var tracer = otel.Tracer("cdn-server")
 
 type Server struct {
 	*grpc.Server
-	config  *config.Config
+	config  Config
 	service supervisor.CDNService
 }
 
 // New returns a new Manager Object.
-func New(config *config.Config, cdnService supervisor.CDNService, opts ...grpc.ServerOption) (*Server, error) {
+func New(config Config, cdnService supervisor.CDNService, opts ...grpc.ServerOption) (*Server, error) {
 	svr := &Server{
 		config:  config,
 		service: cdnService,
@@ -65,7 +64,7 @@ func (css *Server) ObtainSeeds(ctx context.Context, req *cdnsystem.SeedRequest,
 	if pe, ok := peer.FromContext(ctx); ok {
 		clientAddr = pe.Addr.String()
 	}
-	logger.Infof("trigger obtain seed for taskID: %s, url: %s, urlMeta: %+v client: %s", req.TaskId, req.Url, req.UrlMeta, clientAddr)
+	logger.Infof("trigger obtain seed for taskID: %s, url: %s, urlMeta: %s client: %s", req.TaskId, req.Url, req.UrlMeta, clientAddr)
 	var span trace.Span
 	ctx, span = tracer.Start(ctx, constants.SpanObtainSeeds, trace.WithSpanKind(trace.SpanKindServer))
 	defer span.End()
@@ -245,3 +244,7 @@ func (css *Server) Shutdown() error {
 	}
 	return nil
 }
+
+func (css *Server) GetConfig() Config {
+	return css.config
+}
diff --git a/cdn/storedriver/driver.go b/cdn/storedriver/driver.go
index 0c261fe9..5e2ede41 100644
--- a/cdn/storedriver/driver.go
+++ b/cdn/storedriver/driver.go
@@ -34,12 +34,29 @@ import (
 	"d7y.io/dragonfly/v2/pkg/util/stringutils"
 )
 
+var (
+	// m is a map from name to driver builder.
+	m = make(map[string]DriverBuilder)
+)
+
 // DriverBuilder is a function that creates a new storage driver plugin instant with the giving Config.
 type DriverBuilder func(cfg *Config) (Driver, error)
 
-// Register defines an interface to register a driver with specified name.
+func Register(name string, builder DriverBuilder) {
+	m[strings.ToLower(name)] = builder
+}
+
+// Get return a storage manager from manager with specified name.
+func Get(name string) DriverBuilder {
+	if b, ok := m[strings.ToLower(name)]; ok {
+		return b
+	}
+	return nil
+}
+
+// RegisterPlugin defines an interface to register a driver with specified name.
 // All drivers should call this function to register itself to the driverFactory.
-func Register(name string, builder DriverBuilder) error {
+func RegisterPlugin(name string, builder DriverBuilder) error {
 	name = strings.ToLower(name)
 	// plugin builder
 	var f = func(conf interface{}) (plugins.Plugin, error) {
@@ -60,8 +77,8 @@ func Register(name string, builder DriverBuilder) error {
 	return plugins.RegisterPluginBuilder(plugins.StorageDriverPlugin, name, f)
 }
 
-// Get a store from manager with specified name.
-func Get(name string) (Driver, bool) {
+// GetPlugin a store from manager with specified name.
+func GetPlugin(name string) (Driver, bool) {
 	v, ok := plugins.GetPlugin(plugins.StorageDriverPlugin, strings.ToLower(name))
 	if !ok {
 		return nil, false
diff --git a/cdn/storedriver/local/local_driver.go b/cdn/storedriver/local/local_driver.go
index 947759b4..684811a7 100644
--- a/cdn/storedriver/local/local_driver.go
+++ b/cdn/storedriver/local/local_driver.go
@@ -41,11 +41,13 @@ const (
 var fileLocker = synclock.NewLockerPool()
 
 func init() {
-	if err := storedriver.Register(DiskDriverName, NewStorageDriver); err != nil {
+	storedriver.Register(DiskDriverName, NewStorageDriver)
+	storedriver.Register(MemoryDriverName, NewStorageDriver)
+	if err := storedriver.RegisterPlugin(DiskDriverName, NewStorageDriver); err != nil {
 		logger.CoreLogger.Error(err)
 	}
 
-	if err := storedriver.Register(MemoryDriverName, NewStorageDriver); err != nil {
+	if err := storedriver.RegisterPlugin(MemoryDriverName, NewStorageDriver); err != nil {
 		logger.CoreLogger.Error(err)
 	}
 }
@@ -58,6 +60,13 @@ type driver struct {
 
 // NewStorageDriver performs initialization for disk Storage and return a storage Driver.
 func NewStorageDriver(cfg *storedriver.Config) (storedriver.Driver, error) {
+	// prepare the base dir
+	if !filepath.IsAbs(cfg.BaseDir) {
+		return nil, fmt.Errorf("not absolute path: %s", cfg.BaseDir)
+	}
+	if err := fileutils.MkdirAll(cfg.BaseDir); err != nil {
+		return nil, fmt.Errorf("create baseDir %s: %v", cfg.BaseDir, err)
+	}
 	return &driver{
 		BaseDir: cfg.BaseDir,
 	}, nil
diff --git a/cdn/supervisor/cdn/cache_detector.go b/cdn/supervisor/cdn/cache_detector.go
index 3b46b4b4..6e6a01fc 100644
--- a/cdn/supervisor/cdn/cache_detector.go
+++ b/cdn/supervisor/cdn/cache_detector.go
@@ -62,6 +62,9 @@ func (cd *cacheDetector) detectCache(ctx context.Context, seedTask *task.SeedTas
 	defer span.End()
 	result, err = cd.doDetect(ctx, seedTask, fileDigest)
 	if err != nil {
+		if err != storage.ErrTaskNotPersisted {
+			seedTask.Log().Infof("reset storage cache because %v", err)
+		}
 		metadata, err := cd.resetCache(seedTask)
 		if err != nil {
 			return nil, errors.Wrapf(err, "reset cache")
diff --git a/cdn/supervisor/cdn/cache_writer.go b/cdn/supervisor/cdn/cache_writer.go
index 2fef1fd2..44857fba 100644
--- a/cdn/supervisor/cdn/cache_writer.go
+++ b/cdn/supervisor/cdn/cache_writer.go
@@ -31,6 +31,7 @@ import (
 	"d7y.io/dragonfly/v2/cdn/constants"
 	"d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage"
 	"d7y.io/dragonfly/v2/cdn/supervisor/task"
+	logger "d7y.io/dragonfly/v2/internal/dflog"
 	"d7y.io/dragonfly/v2/pkg/ratelimiter/limitreader"
 	"d7y.io/dragonfly/v2/pkg/rpc/base"
 	"d7y.io/dragonfly/v2/pkg/util/digestutils"
@@ -68,14 +69,14 @@ func newCacheWriter(cdnReporter *reporter, metadataManager *metadataManager, cac
 }
 
 // startWriter writes the stream data from the reader to the underlying storage.
-func (cw *cacheWriter) startWriter(ctx context.Context, reader *limitreader.LimitReader, seedTask *task.SeedTask, breakPoint int64) (*downloadMetadata,
-	error) {
+func (cw *cacheWriter) startWriter(ctx context.Context, reader *limitreader.LimitReader, seedTask *task.SeedTask, breakPoint int64,
+	writerRoutineLimit int) (*downloadMetadata, error) {
 	var writeSpan trace.Span
 	ctx, writeSpan = tracer.Start(ctx, constants.SpanWriteData)
 	defer writeSpan.End()
 	// currentSourceFileLength is used to calculate the source file Length dynamically
 	currentSourceFileLength := breakPoint
-	routineCount := calculateRoutineCount(seedTask.SourceFileLength-currentSourceFileLength, seedTask.PieceSize)
+	routineCount := calculateRoutineCount(seedTask.SourceFileLength-currentSourceFileLength, seedTask.PieceSize, writerRoutineLimit)
 	writeSpan.SetAttributes(constants.AttributeWriteGoroutineCount.Int(routineCount))
 	// start writer pool
 	backSourceLength, totalPieceCount, err := cw.doWrite(ctx, reader, seedTask, routineCount, breakPoint)
@@ -108,6 +109,7 @@ func (cw *cacheWriter) doWrite(ctx context.Context, reader io.Reader, seedTask *
 	err error) {
 	// the pieceNum currently have been processed
 	curPieceNum := int32(breakPoint / int64(seedTask.PieceSize))
+	seedTask.Log().Infof("start writing resource to storage from piece %d", curPieceNum)
 	var bufPool = &sync.Pool{
 		New: func() interface{} {
 			return new(bytes.Buffer)
@@ -132,6 +134,7 @@ loop:
 				close(jobCh)
 				return backSourceLength, 0, errors.Errorf("read taskID %s pieceNum %d piece from source failed: %v", seedTask.ID, curPieceNum, err)
 			}
+			seedTask.Log().Debugf("success read %d bytes from source", n)
 			if n == 0 {
 				break loop
 			}
@@ -150,6 +153,7 @@ loop:
 		}
 	}
 	close(jobCh)
+	seedTask.Log().Infof("finish read from resource, backSourceLength %d", backSourceLength)
 	if err := g.Wait(); err != nil {
 		return backSourceLength, 0, errors.Wrapf(err, "write pool")
 	}
@@ -176,6 +180,7 @@ func (cw *cacheWriter) writerPool(ctx context.Context, g *errgroup.Group, routin
 					if err != nil {
 						return errors.Errorf("write taskID %s pieceNum %d to download file failed: %v", p.taskID, p.pieceNum, err)
 					}
+					logger.WithTaskID(p.taskID).Debugf("success write pieceNum %d content to storage", p.pieceNum)
 					// Recycle Buffer
 					bufPool.Put(waitToWriteContent)
 					start := uint64(p.pieceNum) * uint64(p.pieceSize)
@@ -198,6 +203,7 @@ func (cw *cacheWriter) writerPool(ctx context.Context, g *errgroup.Group, routin
 					if err = cw.metadataManager.appendPieceMetadata(p.taskID, pieceRecord); err != nil {
 						return errors.Errorf("write piece meta to piece meta file failed: %v", err)
 					}
+					logger.WithTaskID(p.taskID).Debugf("success write pieceNum %d meta to storage", p.pieceNum)
 					// report piece info
 					if err = cw.cdnReporter.reportPieceMetaRecord(ctx, p.taskID, pieceRecord, DownloaderReport); err != nil {
 						// NOTE: should we do this job again?
@@ -215,8 +221,8 @@ func (cw *cacheWriter) writerPool(ctx context.Context, g *errgroup.Group, routin
 */
 
 // calculateRoutineCount max goroutine count is CDNWriterRoutineLimit
-func calculateRoutineCount(remainingFileLength int64, pieceSize int32) int {
-	routineSize := constants.CDNWriterRoutineLimit
+func calculateRoutineCount(remainingFileLength int64, pieceSize int32, writerRoutineLimit int) int {
+	routineSize := writerRoutineLimit
 	if remainingFileLength < 0 || pieceSize <= 0 {
 		return routineSize
 	}
diff --git a/cdn/supervisor/cdn/config.go b/cdn/supervisor/cdn/config.go
new file mode 100644
index 00000000..e4e59ee0
--- /dev/null
+++ b/cdn/supervisor/cdn/config.go
@@ -0,0 +1,85 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cdn
+
+import (
+	"fmt"
+
+	"d7y.io/dragonfly/v2/pkg/unit"
+)
+
+type Config struct {
+	// SystemReservedBandwidth is the network bandwidth reserved for system software.
+	// default: 20 MB, in format of G(B)/g/M(B)/m/K(B)/k/B, pure number will also be parsed as Byte.
+	SystemReservedBandwidth unit.Bytes `yaml:"systemReservedBandwidth" mapstructure:"systemReservedBandwidth"`
+
+	// MaxBandwidth is the network bandwidth that cdn system can use.
+	// default: 200 MB, in format of G(B)/g/M(B)/m/K(B)/k/B, pure number will also be parsed as Byte.
+	MaxBandwidth unit.Bytes `yaml:"maxBandwidth" mapstructure:"maxBandwidth"`
+
+	// WriterRoutineLimit is the count of write goroutine for one task
+	WriterRoutineLimit int `yaml:"WriterRoutineLimit" mapstructure:"WriterRoutineLimit"`
+}
+
+func DefaultConfig() Config {
+	config := Config{}
+	return config.applyDefaults()
+}
+
+func (c Config) applyDefaults() Config {
+	if c.SystemReservedBandwidth == 0 {
+		c.SystemReservedBandwidth = DefaultSystemReservedBandwidth
+	}
+	if c.MaxBandwidth == 0 {
+		c.MaxBandwidth = DefaultMaxBandwidth
+	}
+	if c.WriterRoutineLimit == 0 {
+		c.WriterRoutineLimit = DefaultWriterRoutineLimit
+	}
+	return c
+}
+
+func (c Config) Validate() []error {
+	var errors []error
+	if c.SystemReservedBandwidth < 0 {
+		errors = append(errors, fmt.Errorf("cdn SystemReservedBandwidth %d can't be a negative number", c.SystemReservedBandwidth))
+	}
+	if c.MaxBandwidth <= 0 {
+		errors = append(errors, fmt.Errorf("cdn MaxBandwidth must be greater than 0, but is: %d", c.MaxBandwidth))
+	}
+	if c.SystemReservedBandwidth >= c.MaxBandwidth {
+		errors = append(errors, fmt.Errorf("cdn MaxBandwidth must be greater than SystemReservedBandwidth, MaxBandwidth is: %d, "+
+			"SystemReservedBandwidth is: %d", c.MaxBandwidth, c.SystemReservedBandwidth))
+	}
+	if c.WriterRoutineLimit <= 0 {
+		errors = append(errors, fmt.Errorf("cdn WriterRoutineLimit %d can't be a negative number", c.WriterRoutineLimit))
+	}
+	return errors
+}
+
+const (
+	// DefaultSystemReservedBandwidth is the default network bandwidth reserved for system software.
+	// unit: MB/s
+	DefaultSystemReservedBandwidth = 20 * unit.MB
+	// DefaultMaxBandwidth is the default network bandwidth that cdn can use.
+	// unit: MB/s
+	DefaultMaxBandwidth = 1 * unit.GB
+)
+
+const (
+	DefaultWriterRoutineLimit = 4
+)
diff --git a/cdn/supervisor/cdn/downloader.go b/cdn/supervisor/cdn/downloader.go
index 66bed511..0bc68b31 100644
--- a/cdn/supervisor/cdn/downloader.go
+++ b/cdn/supervisor/cdn/downloader.go
@@ -39,7 +39,7 @@ func (cm *manager) download(ctx context.Context, seedTask *task.SeedTask, breakP
 			return nil, errors.Wrapf(err, "calculate the breakRange")
 		}
 	}
-	seedTask.Log().Infof("start downloading URL %s at range %s with header %s", seedTask.RawURL, breakRange, seedTask.Header)
+	seedTask.Log().Infof("start downloading %s at range %s with header %s", seedTask.RawURL, breakRange, seedTask.Header)
 	downloadRequest, err := source.NewRequestWithContext(ctx, seedTask.RawURL, seedTask.Header)
 	if err != nil {
 		return nil, errors.Wrap(err, "create download request")
@@ -48,13 +48,14 @@ func (cm *manager) download(ctx context.Context, seedTask *task.SeedTask, breakP
 		downloadRequest.Header.Add(source.Range, breakRange)
 	}
 	body, expireInfo, err := source.DownloadWithExpireInfo(downloadRequest)
-	// update Expire info
-	if err == nil {
-		cm.updateExpireInfo(seedTask.ID, map[string]string{
-			source.LastModified: expireInfo.LastModified,
-			source.ETag:         expireInfo.ETag,
-		})
+	if err != nil {
+		return nil, err
 	}
+	// update Expire info
+	cm.updateExpireInfo(seedTask.ID, map[string]string{
+		source.LastModified: expireInfo.LastModified,
+		source.ETag:         expireInfo.ETag,
+	})
 	return body, err
 }
 
diff --git a/cdn/supervisor/cdn/manager.go b/cdn/supervisor/cdn/manager.go
index 7f928c1b..cc503750 100644
--- a/cdn/supervisor/cdn/manager.go
+++ b/cdn/supervisor/cdn/manager.go
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+//go:generate mockgen -destination ../mocks/cdn/mock_cdn_manager.go -package cdn d7y.io/dragonfly/v2/cdn/supervisor/cdn Manager
+
 package cdn
 
 import (
@@ -27,11 +29,8 @@ import (
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/trace"
 
-	"d7y.io/dragonfly/v2/cdn/config"
 	"d7y.io/dragonfly/v2/cdn/constants"
 	"d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage"
-	_ "d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage/disk"   // nolint
-	_ "d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage/hybrid" // nolint
 	"d7y.io/dragonfly/v2/cdn/supervisor/progress"
 	"d7y.io/dragonfly/v2/cdn/supervisor/task"
 	logger "d7y.io/dragonfly/v2/internal/dflog"
@@ -66,7 +65,7 @@ var tracer = otel.Tracer("cdn-server")
 
 // Manager is an implementation of the interface of Manager.
 type manager struct {
-	cfg             *config.Config
+	config          Config
 	cacheStore      storage.Manager
 	limiter         *ratelimiter.RateLimiter
 	cdnLocker       *synclock.LockerPool
@@ -79,17 +78,17 @@ type manager struct {
 }
 
 // NewManager returns a new Manager.
-func NewManager(cfg *config.Config, cacheStore storage.Manager, progressManager progress.Manager,
+func NewManager(config Config, cacheStore storage.Manager, progressManager progress.Manager,
 	taskManager task.Manager) (Manager, error) {
-	return newManager(cfg, cacheStore, progressManager, taskManager)
+	return newManager(config, cacheStore, progressManager, taskManager)
 }
 
-func newManager(cfg *config.Config, cacheStore storage.Manager, progressManager progress.Manager, taskManager task.Manager) (Manager, error) {
-	rateLimiter := ratelimiter.NewRateLimiter(ratelimiter.TransRate(int64(cfg.MaxBandwidth-cfg.SystemReservedBandwidth)), 2)
+func newManager(config Config, cacheStore storage.Manager, progressManager progress.Manager, taskManager task.Manager) (Manager, error) {
+	rateLimiter := ratelimiter.NewRateLimiter(ratelimiter.TransRate(int64(config.MaxBandwidth-config.SystemReservedBandwidth)), 2)
 	metadataManager := newMetadataManager(cacheStore)
 	cdnReporter := newReporter(progressManager)
 	return &manager{
-		cfg:             cfg,
+		config:          config,
 		cacheStore:      cacheStore,
 		limiter:         rateLimiter,
 		metadataManager: metadataManager,
@@ -168,7 +167,7 @@ func (cm *manager) doTrigger(ctx context.Context, seedTask *task.SeedTask) (*tas
 	reader := limitreader.NewLimitReaderWithLimiterAndDigest(respBody, cm.limiter, fileDigest, digestutils.Algorithms[digestType])
 
 	// forth: write to storage
-	downloadMetadata, err := cm.writer.startWriter(ctx, reader, seedTask, detectResult.BreakPoint)
+	downloadMetadata, err := cm.writer.startWriter(ctx, reader, seedTask, detectResult.BreakPoint, cm.config.WriterRoutineLimit)
 	if err != nil {
 		server.StatSeedFinish(seedTask.ID, seedTask.RawURL, false, err, start, time.Now(), downloadMetadata.backSourceLength,
 			downloadMetadata.realSourceFileLength)
diff --git a/cdn/supervisor/cdn/storage/config.go b/cdn/supervisor/cdn/storage/config.go
new file mode 100644
index 00000000..dc575264
--- /dev/null
+++ b/cdn/supervisor/cdn/storage/config.go
@@ -0,0 +1,116 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package storage
+
+import (
+	"fmt"
+	"path/filepath"
+	"runtime"
+	"time"
+
+	"d7y.io/dragonfly/v2/pkg/basic"
+	"d7y.io/dragonfly/v2/pkg/unit"
+)
+
+type Config struct {
+	StorageMode    string                   `yaml:"storageMode"`
+	GCInitialDelay time.Duration            `yaml:"gcInitialDelay"`
+	GCInterval     time.Duration            `yaml:"gcInterval"`
+	DriverConfigs  map[string]*DriverConfig `yaml:"driverGCConfigs"`
+}
+
+func DefaultConfig() Config {
+	config := Config{}
+	return config.applyDefault()
+}
+
+func (c Config) applyDefault() Config {
+	if c.StorageMode == "" {
+		c.StorageMode = DefaultStorageMode
+	}
+	if c.GCInitialDelay == 0 {
+		c.GCInitialDelay = DefaultGCInitialDelay
+	}
+	if c.GCInterval == 0 {
+		c.GCInterval = DefaultInterval
+	}
+	if len(c.DriverConfigs) == 0 {
+		builder := Get(c.StorageMode)
+		if builder != nil {
+			c.DriverConfigs = builder.DefaultDriverConfigs()
+		}
+	}
+	return c
+}
+
+func (c Config) Validate() []error {
+	var errors []error
+	if ok := IsSupport(c.StorageMode); !ok {
+		errors = append(errors, fmt.Errorf("os %s is not support storage mode %s", runtime.GOOS, c.StorageMode))
+	}
+	if c.GCInitialDelay < 0 {
+		errors = append(errors, fmt.Errorf("storage GCInitialDelay %d can't be a negative number", c.GCInitialDelay))
+	}
+	if c.GCInterval <= 0 {
+		errors = append(errors, fmt.Errorf("storage GCMetaInterval must be greater than 0, but is: %d", c.GCInterval))
+	}
+	if len(c.DriverConfigs) == 0 {
+		errors = append(errors, fmt.Errorf("storage DriverConfigs can not be empty"))
+	}
+	builder := Get(c.StorageMode)
+	if builder == nil {
+		var storageModes []string
+		for s := range managerMap {
+			storageModes = append(storageModes, s)
+		}
+		errors = append(errors, fmt.Errorf("storage StorageMode must in [%s]. but is: %s", storageModes, c.StorageMode))
+	}
+	errors = append(errors, builder.Validate(c.DriverConfigs)...)
+	return errors
+}
+
+type DriverConfig struct {
+	BaseDir        string          `yaml:"baseDir"`
+	DriverGCConfig *DriverGCConfig `yaml:"driverGCConfig"`
+}
+
+// DriverGCConfig driver gc config
+type DriverGCConfig struct {
+	YoungGCThreshold  unit.Bytes    `yaml:"youngGCThreshold"`
+	FullGCThreshold   unit.Bytes    `yaml:"fullGCThreshold"`
+	CleanRatio        int           `yaml:"cleanRatio"`
+	IntervalThreshold time.Duration `yaml:"intervalThreshold"`
+}
+
+const (
+	// DefaultStorageMode is the default storage mode of CDN.
+	DefaultStorageMode = "disk"
+)
+
+const (
+	// DefaultGCInitialDelay is the delay time from the start to the first GC execution.
+	DefaultGCInitialDelay = 0 * time.Second
+
+	// DefaultInterval is the interval time to execute the GC storage.
+	DefaultInterval = 15 * time.Second
+)
+
+var (
+	DefaultDiskBaseDir = filepath.Join(basic.HomeDir, "ftp")
+
+	DefaultMemoryBaseDir = "/dev/shm/dragonfly"
+)
diff --git a/cdn/supervisor/cdn/storage/disk/config.go b/cdn/supervisor/cdn/storage/disk/config.go
new file mode 100644
index 00000000..d59d0ee7
--- /dev/null
+++ b/cdn/supervisor/cdn/storage/disk/config.go
@@ -0,0 +1,73 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package disk
+
+import (
+	"fmt"
+	"time"
+
+	"d7y.io/dragonfly/v2/cdn/storedriver"
+	"d7y.io/dragonfly/v2/cdn/storedriver/local"
+	"d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage"
+	"d7y.io/dragonfly/v2/pkg/unit"
+)
+
+func getDefaultDriverGCConfigs() map[string]*storage.DriverConfig {
+	diskDriverBuilder := storedriver.Get(local.DiskDriverName)
+	if diskDriverBuilder == nil {
+		return nil
+	}
+	diskDriver, err := diskDriverBuilder(&storedriver.Config{BaseDir: storage.DefaultDiskBaseDir})
+	if err != nil {
+		return nil
+	}
+	diskDriverConfig, err := getDiskDriverConfig(diskDriver)
+	if err != nil {
+		return nil
+	}
+	return map[string]*storage.DriverConfig{
+		local.DiskDriverName: diskDriverConfig,
+	}
+}
+
+func validateConfig(driverConfigs map[string]*storage.DriverConfig) []error {
+	var errs []error
+	if len(driverConfigs) != 1 || driverConfigs[local.DiskDriverName] == nil {
+		errs = append(errs, fmt.Errorf("disk storage driver configs should have only one disk driver, but is %v", driverConfigs))
+	}
+	return errs
+}
+
+func getDiskDriverConfig(diskDriver storedriver.Driver) (*storage.DriverConfig, error) {
+	totalSpace, err := diskDriver.GetTotalSpace()
+	if err != nil {
+		return nil, err
+	}
+	yongGCThreshold := 200 * unit.GB
+	if totalSpace > 0 && totalSpace/4 < yongGCThreshold {
+		yongGCThreshold = totalSpace / 4
+	}
+	return &storage.DriverConfig{
+		BaseDir: diskDriver.GetBaseDir(),
+		DriverGCConfig: &storage.DriverGCConfig{
+			YoungGCThreshold:  yongGCThreshold,
+			FullGCThreshold:   25 * unit.GB,
+			IntervalThreshold: 2 * time.Hour,
+			CleanRatio:        1,
+		},
+	}, nil
+}
diff --git a/cdn/supervisor/cdn/storage/disk/disk.go b/cdn/supervisor/cdn/storage/disk/disk.go
index 8de0ed30..4b26b143 100644
--- a/cdn/supervisor/cdn/storage/disk/disk.go
+++ b/cdn/supervisor/cdn/storage/disk/disk.go
@@ -22,14 +22,13 @@ import (
 	"io"
 	"os"
 	"path"
+	"path/filepath"
 	"strings"
-	"time"
 
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 	"go.uber.org/atomic"
 
-	"d7y.io/dragonfly/v2/cdn/config"
 	"d7y.io/dragonfly/v2/cdn/gc"
 	"d7y.io/dragonfly/v2/cdn/storedriver"
 	"d7y.io/dragonfly/v2/cdn/storedriver/local"
@@ -41,71 +40,73 @@ import (
 	"d7y.io/dragonfly/v2/pkg/util/fileutils"
 )
 
-const StorageMode = storage.DiskStorageMode
+const _disk = local.DiskDriverName
 
 var (
 	_ gc.Executor     = (*diskStorageManager)(nil)
 	_ storage.Manager = (*diskStorageManager)(nil)
+	_ storage.Builder = (*diskStorageBuilder)(nil)
 )
 
-func init() {
-	if err := storage.Register(StorageMode, newStorageManager); err != nil {
-		logger.CoreLogger.Error(err)
-	}
+type diskStorageBuilder struct {
+	defaultDriverConfigsFunc func() map[string]*storage.DriverConfig
+	validateFunc             func(map[string]*storage.DriverConfig) []error
 }
 
-func newStorageManager(cfg *config.StorageConfig) (storage.Manager, error) {
-	if len(cfg.DriverConfigs) != 1 {
-		return nil, fmt.Errorf("disk storage manager should have only one disk driver, cfg's driver number is wrong config: %v", cfg)
+func (b *diskStorageBuilder) Build(config storage.Config, taskManager task.Manager) (storage.Manager, error) {
+	diskDriverBuilder := storedriver.Get(local.DiskDriverName)
+	if diskDriverBuilder == nil {
+		return nil, fmt.Errorf("can not find disk driver for disk storage manager")
 	}
-	diskDriver, ok := storedriver.Get(local.DiskDriverName)
-	if !ok {
-		return nil, fmt.Errorf("can not find disk driver for disk storage manager, config is %#v", cfg)
+	diskDriver, err := diskDriverBuilder(&storedriver.Config{
+		BaseDir: config.DriverConfigs[local.DiskDriverName].BaseDir,
+	})
+	if err != nil {
+		return nil, err
 	}
-
 	storageManager := &diskStorageManager{
-		cfg:        cfg,
-		diskDriver: diskDriver,
+		config:      config,
+		diskDriver:  diskDriver,
+		taskManager: taskManager,
 	}
-	gc.Register("diskStorage", cfg.GCInitialDelay, cfg.GCInterval, storageManager)
+	cleaner, err := storage.NewStorageCleaner(config.DriverConfigs[local.DiskDriverName].DriverGCConfig, diskDriver, storageManager, taskManager)
+	if err != nil {
+		return nil, err
+	}
+	storageManager.diskCleaner = cleaner
+	gc.Register("diskStorage", config.GCInitialDelay, config.GCInterval, storageManager)
 	return storageManager, nil
 }
 
-type diskStorageManager struct {
-	cfg         *config.StorageConfig
-	diskDriver  storedriver.Driver
-	cleaner     storage.Cleaner
-	taskManager task.Manager
+func (*diskStorageBuilder) Name() string {
+	return _disk
 }
 
-func (s *diskStorageManager) getDefaultGcConfig() *config.GCConfig {
-	totalSpace, err := s.diskDriver.GetTotalSpace()
-	if err != nil {
-		logger.GcLogger.With("type", "disk").Errorf("get total space of disk: %v", err)
-	}
-	yongGcThreshold := 200 * unit.GB
-	if totalSpace > 0 && totalSpace/4 < yongGcThreshold {
-		yongGcThreshold = totalSpace / 4
-	}
-	return &config.GCConfig{
-		YoungGCThreshold:  yongGcThreshold,
-		FullGCThreshold:   25 * unit.GB,
-		IntervalThreshold: 2 * time.Hour,
-		CleanRatio:        1,
-	}
+func (b *diskStorageBuilder) Validate(driverConfigs map[string]*storage.DriverConfig) []error {
+	return b.validateFunc(driverConfigs)
 }
 
-func (s *diskStorageManager) Initialize(taskManager task.Manager) {
-	s.taskManager = taskManager
-	diskGcConfig := s.cfg.DriverConfigs[local.DiskDriverName].GCConfig
-	if diskGcConfig == nil {
-		diskGcConfig = s.getDefaultGcConfig()
-		logger.GcLogger.With("type", "disk").Warnf("disk gc config is nil, use default gcConfig: %v", diskGcConfig)
-	}
-	s.cleaner, _ = storage.NewStorageCleaner(diskGcConfig, s.diskDriver, s, taskManager)
+func (b *diskStorageBuilder) DefaultDriverConfigs() map[string]*storage.DriverConfig {
+	return b.defaultDriverConfigsFunc()
+}
+
+func init() {
+	// TODO add support OS
+	storage.Register(&diskStorageBuilder{
+		defaultDriverConfigsFunc: getDefaultDriverGCConfigs,
+		validateFunc:             validateConfig,
+	})
+}
+
+type diskStorageManager struct {
+	config      storage.Config
+	diskDriver  storedriver.Driver
+	diskCleaner storage.Cleaner
+	taskManager task.Manager
 }
 
 func (s *diskStorageManager) AppendPieceMetadata(taskID string, pieceRecord *storage.PieceMetaRecord) error {
+	// TODO json
 	return s.diskDriver.PutBytes(storage.GetAppendPieceMetadataRaw(taskID), []byte(pieceRecord.String()+"\n"))
 }
 
@@ -126,32 +127,6 @@ func (s *diskStorageManager) ReadPieceMetaRecords(taskID string) ([]*storage.Pie
 	return result, nil
 }
 
-func (s *diskStorageManager) GC() error {
-	logger.GcLogger.With("type", "disk").Info("start the disk storage gc job")
-	gcTaskIDs, err := s.cleaner.GC("disk", false)
-	if err != nil {
-		logger.GcLogger.With("type", "disk").Error("failed to get gcTaskIDs")
-	}
-	var realGCCount int
-	for _, taskID := range gcTaskIDs {
-		synclock.Lock(taskID, false)
-		// try to ensure the taskID is not using again
-		if _, exist := s.taskManager.Exist(taskID); exist {
-			synclock.UnLock(taskID, false)
-			continue
-		}
-		realGCCount++
-		if err := s.DeleteTask(taskID); err != nil {
-			logger.GcLogger.With("type", "disk").Errorf("failed to delete disk files with taskID(%s): %v", taskID, err)
-			synclock.UnLock(taskID, false)
-			continue
-		}
-		synclock.UnLock(taskID, false)
-	}
-	logger.GcLogger.With("type", "disk").Infof("at most %d tasks can be cleaned up, actual gc %d tasks", len(gcTaskIDs), realGCCount)
-	return nil
-}
-
 func (s *diskStorageManager) WriteDownloadFile(taskID string, offset int64, len int64, data io.Reader) error {
 	raw := storage.GetDownloadRaw(taskID)
 	raw.Offset = offset
@@ -200,13 +175,28 @@ func (s *diskStorageManager) ReadDownloadFile(taskID string) (io.ReadCloser, err
 }
 
 func (s *diskStorageManager) StatDownloadFile(taskID string) (*storedriver.StorageInfo, error) {
-	return s.diskDriver.Stat(storage.GetDownloadRaw(taskID))
+	storageInfo, err := s.diskDriver.Stat(storage.GetDownloadRaw(taskID))
+	if err != nil && os.IsNotExist(err) {
+		return nil, storage.ErrTaskNotPersisted
+	}
+	return storageInfo, err
 }
 
-func (s *diskStorageManager) CreateUploadLink(taskID string) error {
+func (s *diskStorageManager) ResetRepo(seedTask *task.SeedTask) error {
+	if err := s.DeleteTask(seedTask.ID); err != nil {
+		return errors.Errorf("delete task %s files: %v", seedTask.ID, err)
+	}
+	// create download file
+	var downloadPath = s.diskDriver.GetPath(storage.GetDownloadRaw(seedTask.ID))
+	if err := fileutils.MkdirAll(filepath.Dir(downloadPath)); err != nil {
+		return err
+	}
+	if _, err := os.Create(downloadPath); err != nil {
+		return err
+	}
 	// create a soft link from the upload file to the download file
-	if err := fileutils.SymbolicLink(s.diskDriver.GetPath(storage.GetDownloadRaw(taskID)),
-		s.diskDriver.GetPath(storage.GetUploadRaw(taskID))); err != nil {
+	if err := fileutils.SymbolicLink(s.diskDriver.GetPath(storage.GetDownloadRaw(seedTask.ID)),
+		s.diskDriver.GetPath(storage.GetUploadRaw(seedTask.ID))); err != nil {
 		return err
 	}
 	return nil
@@ -232,10 +222,6 @@ func (s *diskStorageManager) DeleteTask(taskID string) error {
 	return nil
 }
 
-func (s *diskStorageManager) ResetRepo(task *task.SeedTask) error {
-	return s.DeleteTask(task.ID)
-}
-
 func (s *diskStorageManager) TryFreeSpace(fileLength int64) (bool, error) {
 	freeSpace, err := s.diskDriver.GetFreeSpace()
 	if err != nil {
@@ -244,19 +230,19 @@ func (s *diskStorageManager) TryFreeSpace(fileLength int64) (bool, error) {
 	if freeSpace > 100*unit.GB && freeSpace.ToNumber() > fileLength {
 		return true, nil
 	}
-
+	// TODO Optimize this code by iterating through the task list
 	remainder := atomic.NewInt64(0)
 	r := &storedriver.Raw{
 		WalkFn: func(filePath string, info os.FileInfo, err error) error {
 			if fileutils.IsRegular(filePath) {
 				taskID := strings.Split(path.Base(filePath), ".")[0]
-				task, exist := s.taskManager.Exist(taskID)
+				seedTask, exist := s.taskManager.Exist(taskID)
 				if exist {
 					var totalLen int64 = 0
-					if task.CdnFileLength > 0 {
-						totalLen = task.CdnFileLength
+					if seedTask.CdnFileLength > 0 {
+						totalLen = seedTask.CdnFileLength
 					} else {
-						totalLen = task.SourceFileLength
+						totalLen = seedTask.SourceFileLength
 					}
 					if totalLen > 0 {
 						remainder.Add(totalLen - info.Size())
@@ -270,9 +256,9 @@ func (s *diskStorageManager) TryFreeSpace(fileLength int64) (bool, error) {
 		return false, err
 	}
 
-	enoughSpace := freeSpace.ToNumber()-remainder.Load() > fileLength
+	enoughSpace := freeSpace.ToNumber()-remainder.Load() > (fileLength + int64(5*unit.GB))
 	if !enoughSpace {
-		if _, err := s.cleaner.GC("disk", true); err != nil {
+		if _, err := s.diskCleaner.GC("disk", true); err != nil {
 			return false, err
 		}
 
@@ -284,7 +270,7 @@ func (s *diskStorageManager) TryFreeSpace(fileLength int64) (bool, error) {
 		if err != nil {
 			return false, err
 		}
-		enoughSpace = freeSpace.ToNumber()-remainder.Load() > fileLength
+		enoughSpace = freeSpace.ToNumber()-remainder.Load() > (fileLength + int64(5*unit.GB))
 	}
 	if !enoughSpace {
 		return false, nil
@@ -292,3 +278,29 @@ func (s *diskStorageManager) TryFreeSpace(fileLength int64) (bool, error) {
 
 	return true, nil
 }
+
+func (s *diskStorageManager) GC() error {
+	logger.GcLogger.With("type", "disk").Info("start the disk storage gc job")
+	gcTaskIDs, err := s.diskCleaner.GC("disk", false)
+	if err != nil {
+		logger.GcLogger.With("type", "disk").Error("failed to get gcTaskIDs")
+	}
+	var realGCCount int
+	for _, taskID := range gcTaskIDs {
+		synclock.Lock(taskID, false)
+		// try to ensure the taskID is not using again
+		if _, exist := s.taskManager.Exist(taskID); exist {
+			synclock.UnLock(taskID, false)
+			continue
+		}
+		realGCCount++
+		if err := s.DeleteTask(taskID); err != nil {
+			logger.GcLogger.With("type", "disk").Errorf("failed to delete disk files with taskID(%s): %v", taskID, err)
+			synclock.UnLock(taskID, false)
+			continue
+		}
+		synclock.UnLock(taskID, false)
+	}
+	logger.GcLogger.With("type", "disk").Infof("at most %d tasks can be cleaned up, actual gc %d tasks", len(gcTaskIDs), realGCCount)
+	return nil
+}
diff --git a/cdn/supervisor/cdn/storage/helper_darwin.go b/cdn/supervisor/cdn/storage/helper_darwin.go
index e2145fae..a0f45d2c 100644
--- a/cdn/supervisor/cdn/storage/helper_darwin.go
+++ b/cdn/supervisor/cdn/storage/helper_darwin.go
@@ -17,7 +17,7 @@
 package storage
 
 func IsSupport(mode string) bool {
-	if mode == DiskStorageMode {
+	if mode == "disk" {
 		return true
 	}
 	return false
diff --git a/cdn/supervisor/cdn/storage/hybrid/config.go b/cdn/supervisor/cdn/storage/hybrid/config.go
new file mode 100644
index 00000000..e9958619
--- /dev/null
+++ b/cdn/supervisor/cdn/storage/hybrid/config.go
@@ -0,0 +1,99 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package hybrid
+
+import (
+	"fmt"
+	"time"
+
+	"d7y.io/dragonfly/v2/cdn/storedriver"
+	"d7y.io/dragonfly/v2/cdn/storedriver/local"
+	"d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage"
+	"d7y.io/dragonfly/v2/pkg/unit"
+)
+
+func getDefaultDriverGCConfigs() map[string]*storage.DriverConfig {
+	diskDriver, err := local.NewStorageDriver(&storedriver.Config{BaseDir: storage.DefaultDiskBaseDir})
+	if err != nil {
+		return nil
+	}
+	memoryDriver, err := local.NewStorageDriver(&storedriver.Config{BaseDir: storage.DefaultMemoryBaseDir})
+	if err != nil {
+		return nil
+	}
+	diskDriverConfig, err := getDiskDriverConfig(diskDriver)
+	if err != nil {
+		return nil
+	}
+	memoryDriverConfig, err := getMemoryDriverConfig(memoryDriver)
+	if err != nil {
+		return nil
+	}
+	return map[string]*storage.DriverConfig{
+		local.DiskDriverName:   diskDriverConfig,
+		local.MemoryDriverName: memoryDriverConfig,
+	}
+}
+
+func validateConfig(driverConfigs map[string]*storage.DriverConfig) []error {
+	var errs []error
+	if len(driverConfigs) != 2 || driverConfigs[local.DiskDriverName] == nil || driverConfigs[local.MemoryDriverName] == nil {
+		errs = append(errs, fmt.Errorf("hybrid storage manager should have both disk and memory driver, but is: %v", driverConfigs))
+	}
+	return errs
+}
+
+func getDiskDriverConfig(diskDriver storedriver.Driver) (*storage.DriverConfig, error) {
+	totalSpace, err := diskDriver.GetTotalSpace()
+	if err != nil {
+		return nil, err
+	}
+	yongGCThreshold := 200 * unit.GB
+	if totalSpace > 0 && totalSpace/4 < yongGCThreshold {
+		yongGCThreshold = totalSpace / 4
+	}
+	return &storage.DriverConfig{
+		BaseDir: diskDriver.GetBaseDir(),
+		DriverGCConfig: &storage.DriverGCConfig{
+			YoungGCThreshold:  yongGCThreshold,
+			FullGCThreshold:   25 * unit.GB,
+			IntervalThreshold: 2 * time.Hour,
+			CleanRatio:        1,
+		},
+	}, nil
+}
+
+func getMemoryDriverConfig(memoryDriver storedriver.Driver) (*storage.DriverConfig, error) {
+	// determine whether the shared cache can be used
+	diff := unit.Bytes(0)
+	totalSpace, err := memoryDriver.GetTotalSpace()
+	if err != nil {
+		return nil, err
+	}
+	if totalSpace < 72*unit.GB {
+		diff = 72*unit.GB - totalSpace
+	}
+	return &storage.DriverConfig{
+		BaseDir: memoryDriver.GetBaseDir(),
+		DriverGCConfig: &storage.DriverGCConfig{
+			YoungGCThreshold:  10*unit.GB + diff,
+			FullGCThreshold:   2*unit.GB + diff,
+			CleanRatio:        3,
+			IntervalThreshold: 2 * time.Hour,
+		},
+	}, nil
+}
diff --git a/cdn/supervisor/cdn/storage/hybrid/hybrid.go b/cdn/supervisor/cdn/storage/hybrid/hybrid.go
index a172a791..1b5957c8 100644
--- a/cdn/supervisor/cdn/storage/hybrid/hybrid.go
+++ b/cdn/supervisor/cdn/storage/hybrid/hybrid.go
@@ -24,12 +24,10 @@ import (
 	"path"
 	"strings"
 	"sync"
-	"time"
 
 	"github.com/pkg/errors"
 	"go.uber.org/atomic"
 
-	"d7y.io/dragonfly/v2/cdn/config"
 	"d7y.io/dragonfly/v2/cdn/gc"
 	"d7y.io/dragonfly/v2/cdn/storedriver"
 	"d7y.io/dragonfly/v2/cdn/storedriver/local"
@@ -41,101 +39,87 @@ import (
 	"d7y.io/dragonfly/v2/pkg/util/fileutils"
 )
 
-const StorageMode = storage.HybridStorageMode
+const _hybrid = "hybrid"
 
 const secureLevel = 500 * unit.MB
 
-var _ storage.Manager = (*hybridStorageManager)(nil)
-var _ gc.Executor = (*hybridStorageManager)(nil)
+var (
+	_ storage.Manager = (*hybridStorageManager)(nil)
+	_ gc.Executor     = (*hybridStorageManager)(nil)
+	_ storage.Builder = (*hybridStorageBuilder)(nil)
+)
 
-func init() {
-	if err := storage.Register(StorageMode, newStorageManager); err != nil {
-		logger.CoreLogger.Error(err)
-	}
+type hybridStorageBuilder struct {
+	defaultDriverConfigsFunc func() map[string]*storage.DriverConfig
+	validateFunc             func(map[string]*storage.DriverConfig) []error
 }
 
-// NewStorageManager performs initialization for storage manager and return a storage Manager.
-func newStorageManager(cfg *config.StorageConfig) (storage.Manager, error) {
-	if len(cfg.DriverConfigs) != 2 {
-		return nil, fmt.Errorf("disk storage manager should have two driver, cfg's driver number is wrong : %v", cfg)
+func (*hybridStorageBuilder) Build(config storage.Config, taskManager task.Manager) (storage.Manager, error) {
+	diskDriverBuilder := storedriver.Get(local.DiskDriverName)
+	if diskDriverBuilder == nil {
+		return nil, fmt.Errorf("can not find disk driver for hybrid storage manager")
+	}
+	diskDriver, err := diskDriverBuilder(&storedriver.Config{
+		BaseDir: config.DriverConfigs[local.DiskDriverName].BaseDir,
+	})
+	if err != nil {
+		return nil, err
 	}
-	diskDriver, ok := storedriver.Get(local.DiskDriverName)
-	if !ok {
-		return nil, fmt.Errorf("can not find disk driver for hybrid storage manager, config is %v", cfg)
+	memoryDriverBuilder := storedriver.Get(local.MemoryDriverName)
+	if memoryDriverBuilder == nil {
+		return nil, fmt.Errorf("can not find memory driver for hybrid storage manager")
 	}
-	memoryDriver, ok := storedriver.Get(local.MemoryDriverName)
-	if !ok {
-		return nil, fmt.Errorf("can not find memory driver for hybrid storage manager, config %v", cfg)
+	memoryDriver, err := memoryDriverBuilder(&storedriver.Config{
+		BaseDir: config.DriverConfigs[local.MemoryDriverName].BaseDir,
+	})
+	if err != nil {
+		return nil, err
 	}
 	storageManager := &hybridStorageManager{
-		cfg:          cfg,
+		config:       config,
 		memoryDriver: memoryDriver,
 		diskDriver:   diskDriver,
-		hasShm:       true,
+		taskManager:  taskManager,
 		shmSwitch:    newShmSwitch(),
+		hasShm:       true,
+	}
+
+	diskDriverCleaner, err := storage.NewStorageCleaner(config.DriverConfigs[local.DiskDriverName].DriverGCConfig, diskDriver, storageManager, taskManager)
+	if err != nil {
+		return nil, err
 	}
-	gc.Register("hybridStorage", cfg.GCInitialDelay, cfg.GCInterval, storageManager)
+	memoryDriverCleaner, err := storage.NewStorageCleaner(config.DriverConfigs[local.MemoryDriverName].DriverGCConfig, memoryDriver, storageManager, taskManager)
+	if err != nil {
+		return nil, err
+	}
+	storageManager.diskDriverCleaner = diskDriverCleaner
+	storageManager.memoryDriverCleaner = memoryDriverCleaner
+
+	gc.Register("hybridStorage", config.GCInitialDelay, config.GCInterval, storageManager)
 	return storageManager, nil
 }
 
-func (h *hybridStorageManager) Initialize(taskManager task.Manager) {
-	h.taskManager = taskManager
-	diskGcConfig := h.cfg.DriverConfigs[local.DiskDriverName].GCConfig
+func (*hybridStorageBuilder) Name() string {
+	return _hybrid
+}
 
-	if diskGcConfig == nil {
-		diskGcConfig = h.getDiskDefaultGcConfig()
-		logger.GcLogger.With("type", "hybrid").Warnf("disk gc config is nil, use default gcConfig: %v", diskGcConfig)
-	}
-	h.diskDriverCleaner, _ = storage.NewStorageCleaner(diskGcConfig, h.diskDriver, h, taskManager)
-	memoryGcConfig := h.cfg.DriverConfigs[local.MemoryDriverName].GCConfig
-	if memoryGcConfig == nil {
-		memoryGcConfig = h.getMemoryDefaultGcConfig()
-		logger.GcLogger.With("type", "hybrid").Warnf("memory gc config is nil, use default gcConfig: %v", diskGcConfig)
-	}
-	h.memoryDriverCleaner, _ = storage.NewStorageCleaner(memoryGcConfig, h.memoryDriver, h, taskManager)
-	logger.GcLogger.With("type", "hybrid").Info("success initialize hybrid cleaners")
+func (b *hybridStorageBuilder) Validate(driverConfigs map[string]*storage.DriverConfig) []error {
+	return b.validateFunc(driverConfigs)
 }
 
-func (h *hybridStorageManager) getDiskDefaultGcConfig() *config.GCConfig {
-	totalSpace, err := h.diskDriver.GetTotalSpace()
-	if err != nil {
-		logger.GcLogger.With("type", "hybrid").Errorf("failed to get total space of disk: %v", err)
-	}
-	yongGcThreshold := 200 * unit.GB
-	if totalSpace > 0 && totalSpace/4 < yongGcThreshold {
-		yongGcThreshold = totalSpace / 4
-	}
-	return &config.GCConfig{
-		YoungGCThreshold:  yongGcThreshold,
-		FullGCThreshold:   25 * unit.GB,
-		IntervalThreshold: 2 * time.Hour,
-		CleanRatio:        1,
-	}
+func (b *hybridStorageBuilder) DefaultDriverConfigs() map[string]*storage.DriverConfig {
+	return b.defaultDriverConfigsFunc()
 }
 
-func (h *hybridStorageManager) getMemoryDefaultGcConfig() *config.GCConfig {
-	// determine whether the shared cache can be used
-	diff := unit.Bytes(0)
-	totalSpace, err := h.memoryDriver.GetTotalSpace()
-	if err != nil {
-		logger.GcLogger.With("type", "hybrid").Errorf("failed to get total space of memory: %v", err)
-	}
-	if totalSpace < 72*unit.GB {
-		diff = 72*unit.GB - totalSpace
-	}
-	if diff >= totalSpace {
-		h.hasShm = false
-	}
-	return &config.GCConfig{
-		YoungGCThreshold:  10*unit.GB + diff,
-		FullGCThreshold:   2*unit.GB + diff,
-		CleanRatio:        3,
-		IntervalThreshold: 2 * time.Hour,
-	}
+func init() {
+	storage.Register(&hybridStorageBuilder{
+		defaultDriverConfigsFunc: getDefaultDriverGCConfigs,
+		validateFunc:             validateConfig,
+	})
 }
 
 type hybridStorageManager struct {
-	cfg                 *config.StorageConfig
+	config              storage.Config
 	memoryDriver        storedriver.Driver
 	diskDriver          storedriver.Driver
 	diskDriverCleaner   storage.Cleaner
diff --git a/cdn/supervisor/cdn/storage/mock/mock_storage_manager.go b/cdn/supervisor/cdn/storage/mock/mock_storage_manager.go
index 0c9d7989..c56d659b 100644
--- a/cdn/supervisor/cdn/storage/mock/mock_storage_manager.go
+++ b/cdn/supervisor/cdn/storage/mock/mock_storage_manager.go
@@ -65,18 +65,6 @@ func (mr *MockManagerMockRecorder) DeleteTask(arg0 interface{}) *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTask", reflect.TypeOf((*MockManager)(nil).DeleteTask), arg0)
 }
 
-// Initialize mocks base method.
-func (m *MockManager) Initialize(arg0 task.Manager) {
-	m.ctrl.T.Helper()
-	m.ctrl.Call(m, "Initialize", arg0)
-}
-
-// Initialize indicates an expected call of Initialize.
-func (mr *MockManagerMockRecorder) Initialize(arg0 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Initialize", reflect.TypeOf((*MockManager)(nil).Initialize), arg0)
-}
-
 // ReadDownloadFile mocks base method.
 func (m *MockManager) ReadDownloadFile(arg0 string) (io.ReadCloser, error) {
 	m.ctrl.T.Helper()
diff --git a/cdn/supervisor/cdn/storage/storage.go b/cdn/supervisor/cdn/storage/storage.go
index e384e5cb..4e9c3001 100644
--- a/cdn/supervisor/cdn/storage/storage.go
+++ b/cdn/supervisor/cdn/storage/storage.go
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 //go:generate mockgen -destination ./mock/mock_storage_manager.go -package mock d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage Manager
 
 package storage
@@ -20,28 +21,29 @@ package storage
 import (
 	"fmt"
 	"io"
-	"reflect"
 	"strconv"
 	"strings"
-	"time"
 
-	"github.com/mitchellh/mapstructure"
 	"github.com/pkg/errors"
-	"gopkg.in/yaml.v3"
 
-	"d7y.io/dragonfly/v2/cdn/config"
-	"d7y.io/dragonfly/v2/cdn/plugins"
 	"d7y.io/dragonfly/v2/cdn/storedriver"
 	"d7y.io/dragonfly/v2/cdn/supervisor/task"
-	"d7y.io/dragonfly/v2/pkg/unit"
 	"d7y.io/dragonfly/v2/pkg/util/rangeutils"
 )
 
+var (
+	// managerMap is a map from name to storageManager builder.
+	managerMap = make(map[string]Builder)
+)
+
+var (
+	ErrTaskNotPersisted = errors.New("task is not persisted")
+)
+
 type Manager interface {
-	Initialize(taskManager task.Manager)
 
 	// ResetRepo reset the storage of task
-	ResetRepo(*task.SeedTask) error
+	ResetRepo(task *task.SeedTask) error
 
 	// StatDownloadFile stat download file info, if task file is not exist on storage, return errTaskNotPersisted
 	StatDownloadFile(taskID string) (*storedriver.StorageInfo, error)
@@ -156,126 +158,38 @@ func ParsePieceMetaRecord(value string) (record *PieceMetaRecord, err error) {
 	}, nil
 }
 
-type managerPlugin struct {
-	// name is a unique identifier, you can also name it ID.
-	name string
-	// instance holds a manger instant which implements the interface of Manager.
-	instance Manager
-}
-
-func (m *managerPlugin) Type() plugins.PluginType {
-	return plugins.StorageManagerPlugin
-}
-
-func (m *managerPlugin) Name() string {
-	return m.name
-}
-
-func (m *managerPlugin) ResetRepo(task *task.SeedTask) error {
-	return m.instance.ResetRepo(task)
-}
-
-func (m *managerPlugin) StatDownloadFile(path string) (*storedriver.StorageInfo, error) {
-	return m.instance.StatDownloadFile(path)
-}
-
-func (m *managerPlugin) WriteDownloadFile(taskID string, offset int64, len int64, data io.Reader) error {
-	return m.instance.WriteDownloadFile(taskID, offset, len, data)
-}
-
-func (m *managerPlugin) ReadDownloadFile(taskID string) (io.ReadCloser, error) {
-	return m.instance.ReadDownloadFile(taskID)
-}
-
-func (m *managerPlugin) ReadFileMetadata(taskID string) (*FileMetadata, error) {
-	return m.instance.ReadFileMetadata(taskID)
-}
-
-func (m *managerPlugin) WriteFileMetadata(taskID string, data *FileMetadata) error {
-	return m.instance.WriteFileMetadata(taskID, data)
-}
-
-func (m *managerPlugin) WritePieceMetaRecords(taskID string, records []*PieceMetaRecord) error {
-	return m.instance.WritePieceMetaRecords(taskID, records)
-}
-
-func (m *managerPlugin) AppendPieceMetadata(taskID string, record *PieceMetaRecord) error {
-	return m.instance.AppendPieceMetadata(taskID, record)
+// Builder creates a balancer.
+type Builder interface {
+	// Build creates a new balancer with the ClientConn.
+	Build(cfg Config, taskManager task.Manager) (Manager, error)
+	// Name returns the name of balancers built by this builder.
+	// It will be used to pick balancers (for example in service config).
+	Name() string
+	// Validate driver configs
+	Validate(map[string]*DriverConfig) []error
+	// DefaultDriverConfigs default driver config
+	DefaultDriverConfigs() map[string]*DriverConfig
 }
 
-func (m *managerPlugin) ReadPieceMetaRecords(taskID string) ([]*PieceMetaRecord, error) {
-	return m.instance.ReadPieceMetaRecords(taskID)
-}
-
-func (m *managerPlugin) DeleteTask(taskID string) error {
-	return m.instance.DeleteTask(taskID)
-}
-
-// ManagerBuilder is a function that creates a new storage manager plugin instant with the giving conf.
-type ManagerBuilder func(cfg *config.StorageConfig) (Manager, error)
-
-// Register defines an interface to register a storage manager with specified name.
-// All storage managers should call this function to register itself to the storage manager factory.
-func Register(name string, builder ManagerBuilder) error {
-	name = strings.ToLower(name)
-	// plugin builder
-	var f = func(conf interface{}) (plugins.Plugin, error) {
-		cfg := &config.StorageConfig{}
-		decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
-			DecodeHook: mapstructure.ComposeDecodeHookFunc(func(from, to reflect.Type, v interface{}) (interface{}, error) {
-				switch to {
-				case reflect.TypeOf(unit.B),
-					reflect.TypeOf(time.Second):
-					b, _ := yaml.Marshal(v)
-					p := reflect.New(to)
-					if err := yaml.Unmarshal(b, p.Interface()); err != nil {
-						return nil, err
-					}
-					return p.Interface(), nil
-				default:
-					return v, nil
-				}
-			}),
-			Result: cfg,
-		})
-		if err != nil {
-			return nil, fmt.Errorf("create decoder: %v", err)
-		}
-		err = decoder.Decode(conf)
-		if err != nil {
-			return nil, fmt.Errorf("parse config: %v", err)
-		}
-		return newManagerPlugin(name, builder, cfg)
-	}
-	return plugins.RegisterPluginBuilder(plugins.StorageManagerPlugin, name, f)
+// Register defines an interface to register a storage manager builder.
+// All storage managers should call this function to register its builder to the storage manager factory.
+func Register(builder Builder) {
+	managerMap[strings.ToLower(builder.Name())] = builder
 }
 
-func newManagerPlugin(name string, builder ManagerBuilder, cfg *config.StorageConfig) (plugins.Plugin, error) {
-	if name == "" || builder == nil {
-		return nil, fmt.Errorf("storage manager plugin's name and builder cannot be nil")
-	}
-
-	instant, err := builder(cfg)
-	if err != nil {
-		return nil, fmt.Errorf("init storage manager %s: %v", name, err)
+// Get return a storage manager from manager with specified name.
+func Get(name string) Builder {
+	if b, ok := managerMap[strings.ToLower(name)]; ok {
+		return b
 	}
-
-	return &managerPlugin{
-		name:     name,
-		instance: instant,
-	}, nil
+	return nil
 }
 
-// Get a storage manager from manager with specified name.
-func Get(name string) (Manager, bool) {
-	v, ok := plugins.GetPlugin(plugins.StorageManagerPlugin, strings.ToLower(name))
-	if !ok {
-		return nil, false
+func NewManager(config Config, taskManager task.Manager) (Manager, error) {
+	// Initialize storage manager
+	storageManagerBuilder := Get(config.StorageMode)
+	if storageManagerBuilder == nil {
+		return nil, fmt.Errorf("can not find storage manager mode %s", config.StorageMode)
 	}
-	return v.(*managerPlugin).instance, true
+	return storageManagerBuilder.Build(config, taskManager)
 }
-
-const (
-	HybridStorageMode = "hybrid"
-	DiskStorageMode   = "disk"
-)
diff --git a/cdn/supervisor/cdn/storage/storage_gc.go b/cdn/supervisor/cdn/storage/storage_gc.go
index cd86890a..7b045ed0 100644
--- a/cdn/supervisor/cdn/storage/storage_gc.go
+++ b/cdn/supervisor/cdn/storage/storage_gc.go
@@ -25,7 +25,6 @@ import (
 	"github.com/emirpasic/gods/maps/treemap"
 	godsutils "github.com/emirpasic/gods/utils"
 
-	"d7y.io/dragonfly/v2/cdn/config"
 	"d7y.io/dragonfly/v2/cdn/storedriver"
 	"d7y.io/dragonfly/v2/cdn/supervisor/task"
 	logger "d7y.io/dragonfly/v2/internal/dflog"
@@ -37,15 +36,15 @@ type Cleaner interface {
 }
 
 type cleaner struct {
-	cfg            *config.GCConfig
+	config         *DriverGCConfig
 	driver         storedriver.Driver
 	taskManager    task.Manager
 	storageManager Manager
 }
 
-func NewStorageCleaner(cfg *config.GCConfig, driver storedriver.Driver, storageManager Manager, taskManager task.Manager) (Cleaner, error) {
+func NewStorageCleaner(config *DriverGCConfig, driver storedriver.Driver, storageManager Manager, taskManager task.Manager) (Cleaner, error) {
 	return &cleaner{
-		cfg:            cfg,
+		config:         config,
 		driver:         driver,
 		taskManager:    taskManager,
 		storageManager: storageManager,
@@ -70,10 +69,10 @@ func (cleaner *cleaner) GC(storagePattern string, force bool) ([]string, error)
 	}
 	fullGC := force
 	if !fullGC {
-		if freeSpace > cleaner.cfg.YoungGCThreshold {
+		if freeSpace > cleaner.config.YoungGCThreshold {
 			return nil, nil
 		}
-		if freeSpace <= cleaner.cfg.FullGCThreshold {
+		if freeSpace <= cleaner.config.FullGCThreshold {
 			fullGC = true
 		}
 	}
@@ -146,7 +145,7 @@ func (cleaner *cleaner) sortInert(gapTasks, intervalTasks *treemap.Map, metadata
 	gap := timeutils.CurrentTimeMillis() - metadata.AccessTime
 
 	if metadata.Interval > 0 &&
-		gap <= metadata.Interval+(int64(cleaner.cfg.IntervalThreshold.Seconds())*int64(time.Millisecond)) {
+		gap <= metadata.Interval+(int64(cleaner.config.IntervalThreshold.Seconds())*int64(time.Millisecond)) {
 		info, err := cleaner.storageManager.StatDownloadFile(metadata.TaskID)
 		if err != nil {
 			return err
@@ -187,6 +186,6 @@ func (cleaner *cleaner) getGCTasks(gapTasks, intervalTasks *treemap.Map) []strin
 		}
 	}
 
-	gcLen := (len(gcTasks)*cleaner.cfg.CleanRatio + 9) / 10
+	gcLen := (len(gcTasks)*cleaner.config.CleanRatio + 9) / 10
 	return gcTasks[0:gcLen]
 }
diff --git a/cdn/supervisor/progress/manager.go b/cdn/supervisor/progress/manager.go
index a8ae66c2..ffc332aa 100644
--- a/cdn/supervisor/progress/manager.go
+++ b/cdn/supervisor/progress/manager.go
@@ -133,6 +133,7 @@ func (pm *manager) PublishTask(ctx context.Context, taskID string, seedTask *tas
 	span := trace.SpanFromContext(ctx)
 	recordBytes, _ := json.Marshal(seedTask)
 	span.AddEvent(constants.EventPublishTask, trace.WithAttributes(constants.AttributeSeedTask.String(string(recordBytes))))
+	// task status must be updated before removing the subscribers
 	if err := pm.taskManager.Update(taskID, seedTask); err != nil {
 		return err
 	}
diff --git a/cdn/supervisor/task/config.go b/cdn/supervisor/task/config.go
new file mode 100644
index 00000000..fa6d941c
--- /dev/null
+++ b/cdn/supervisor/task/config.go
@@ -0,0 +1,98 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package task
+
+import (
+	"fmt"
+	"time"
+)
+
+type Config struct {
+	// GCInitialDelay is the delay time from the start to the first GC execution.
+	// default: 6s
+	GCInitialDelay time.Duration `yaml:"gcInitialDelay" mapstructure:"gcInitialDelay"`
+
+	// GCMetaInterval is the interval time to execute GC meta.
+	// default: 2min
+	GCMetaInterval time.Duration `yaml:"gcMetaInterval" mapstructure:"gcMetaInterval"`
+
+	// ExpireTime when a task is not accessed within the ExpireTime,
+	// and it will be treated to be expired.
+	// default: 3min
+	ExpireTime time.Duration `yaml:"taskExpireTime" mapstructure:"taskExpireTime"`
+
+	// FailAccessInterval is the interval time after failed to access the URL.
+	// unit: minutes
+	// default: 30
+	FailAccessInterval time.Duration `yaml:"failAccessInterval" mapstructure:"failAccessInterval"`
+}
+
+func DefaultConfig() Config {
+	config := Config{}
+	return config.applyDefaults()
+}
+
+func (c Config) applyDefaults() Config {
+	if c.GCInitialDelay == 0 {
+		c.GCInitialDelay = DefaultGCInitialDelay
+	}
+	if c.GCMetaInterval == 0 {
+		c.GCMetaInterval = DefaultGCMetaInterval
+	}
+	if c.ExpireTime == 0 {
+		c.ExpireTime = DefaultExpireTime
+	}
+	if c.FailAccessInterval == 0 {
+		c.FailAccessInterval = DefaultFailAccessInterval
+	}
+	return c
+}
+
+func (c Config) Validate() []error {
+	var errors []error
+	if c.GCInitialDelay < 0 {
+		errors = append(errors, fmt.Errorf("task GCInitialDelay %d can't be a negative number", c.GCInitialDelay))
+	}
+	if c.GCMetaInterval <= 0 {
+		errors = append(errors, fmt.Errorf("task GCMetaInterval must be greater than 0, but is: %d", c.GCMetaInterval))
+	}
+	if c.ExpireTime <= 0 {
+		errors = append(errors, fmt.Errorf("task ExpireTime must be greater than 0, but is: %d", c.ExpireTime))
+	}
+	if c.FailAccessInterval <= 0 {
+		errors = append(errors, fmt.Errorf("task FailAccessInterval must be greater than 0, but is: %d", c.FailAccessInterval))
+	}
+	return errors
+}
+
+const (
+	// DefaultFailAccessInterval is the interval time after failed to access the URL.
+	DefaultFailAccessInterval = 3 * time.Minute
+)
+
+// gc config
+const (
+	// DefaultGCInitialDelay is the delay time from the start to the first GC execution.
+	DefaultGCInitialDelay = 6 * time.Second
+
+	// DefaultGCMetaInterval is the interval time to execute the GC meta.
+	DefaultGCMetaInterval = 2 * time.Minute
+
+	// DefaultExpireTime when a task is not accessed within the ExpireTime,
+	// and it will be treated to be expired.
+	DefaultExpireTime = 30 * time.Minute
+)
diff --git a/cdn/supervisor/task/manager.go b/cdn/supervisor/task/manager.go
index d65c2191..1bc5498c 100644
--- a/cdn/supervisor/task/manager.go
+++ b/cdn/supervisor/task/manager.go
@@ -24,7 +24,6 @@ import (
 
 	"github.com/pkg/errors"
 
-	"d7y.io/dragonfly/v2/cdn/config"
 	"d7y.io/dragonfly/v2/cdn/gc"
 	logger "d7y.io/dragonfly/v2/internal/dflog"
 	"d7y.io/dragonfly/v2/internal/util"
@@ -85,14 +84,14 @@ func IsTaskNotFound(err error) bool {
 
 // manager is an implementation of the interface of Manager.
 type manager struct {
-	config                  *config.Config
+	config                  Config
 	taskStore               sync.Map
 	accessTimeMap           sync.Map
 	taskURLUnreachableStore sync.Map
 }
 
 // NewManager returns a new Manager Object.
-func NewManager(config *config.Config) (Manager, error) {
+func NewManager(config Config) (Manager, error) {
 
 	manager := &manager{
 		config: config,
@@ -233,7 +232,7 @@ const (
 )
 
 func (tm *manager) GC() error {
-	logger.Info("start the task meta gc job")
+	logger.GcLogger.Info("start the task meta gc job")
 	startTime := time.Now()
 
 	totalTaskNums := 0
@@ -244,7 +243,7 @@ func (tm *manager) GC() error {
 		synclock.Lock(taskID, false)
 		defer synclock.UnLock(taskID, false)
 		atime := value.(time.Time)
-		if time.Since(atime) < tm.config.TaskExpireTime {
+		if time.Since(atime) < tm.config.ExpireTime {
 			return true
 		}
 
diff --git a/cmd/cdn/cmd/root.go b/cmd/cdn/cmd/root.go
index 67786f4c..dc4dd041 100644
--- a/cmd/cdn/cmd/root.go
+++ b/cmd/cdn/cmd/root.go
@@ -17,11 +17,11 @@
 package cmd
 
 import (
+	"fmt"
 	"os"
 
 	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
-	"gopkg.in/yaml.v3"
 
 	"d7y.io/dragonfly/v2/cdn"
 	"d7y.io/dragonfly/v2/cdn/config"
@@ -34,7 +34,8 @@ import (
 )
 
 var (
-	cfg *config.Config
+	deprecatedConfig *config.DeprecatedConfig
+	cfg              *config.Config
 )
 
 // rootCmd represents the base command when called without any subcommands
@@ -48,8 +49,9 @@ from remote source repeatedly.`,
 	DisableAutoGenTag: true,
 	SilenceUsage:      true,
 	RunE: func(cmd *cobra.Command, args []string) error {
+		cfg = deprecatedConfig.Convert()
 		// Initialize dfpath
-		d, err := initDfpath(cfg.BaseProperties)
+		d, err := initDfpath(cfg.LogDir, cfg.WorkHome)
 		if err != nil {
 			return err
 		}
@@ -77,18 +79,18 @@ func Execute() {
 
 func init() {
 	// Initialize default cdn system config
-	cfg = config.New()
+	deprecatedConfig = config.NewDeprecatedConfig()
 	// Initialize cobra
-	dependency.InitCobra(rootCmd, true, cfg)
+	dependency.InitCobra(rootCmd, true, deprecatedConfig)
 }
 
-func initDfpath(cfg *config.BaseProperties) (dfpath.Dfpath, error) {
+func initDfpath(logDir, workHome string) (dfpath.Dfpath, error) {
 	var options []dfpath.Option
 	if cfg.LogDir != "" {
-		options = append(options, dfpath.WithLogDir(cfg.LogDir))
+		options = append(options, dfpath.WithLogDir(logDir))
 	}
 	if cfg.WorkHome != "" {
-		options = append(options, dfpath.WithWorkHome(cfg.WorkHome))
+		options = append(options, dfpath.WithWorkHome(workHome))
 	}
 
 	return dfpath.New(options...)
@@ -96,10 +98,12 @@ func initDfpath(cfg *config.BaseProperties) (dfpath.Dfpath, error) {
 
 func runCdnSystem() error {
 	logger.Infof("Version:\n%s", version.Version())
+	// validate config
+	if errs := cfg.Validate(); len(errs) != 0 {
+		return fmt.Errorf("failed to validate cdn config:\n%s", errs)
+	}
 	// cdn system config values
-	s, _ := yaml.Marshal(cfg)
-
-	logger.Infof("cdn system configuration:\n%s", string(s))
+	logger.Infof("cdn system configuration:\n%s", cfg)
 
 	ff := dependency.InitMonitor(cfg.Verbose, cfg.PProfPort, cfg.Telemetry)
 	defer ff()
diff --git a/cmd/cdn/main.go b/cmd/cdn/main.go
index 8c6c20f6..b9086ccd 100644
--- a/cmd/cdn/main.go
+++ b/cmd/cdn/main.go
@@ -17,13 +17,12 @@
 package main
 
 import (
-	"d7y.io/dragonfly/v2/cmd/cdn/cmd"
+	_ "d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage/disk"   //nolint:gci    // Register disk storage manager
+	_ "d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage/hybrid" // Register hybrid storage manager
+	_ "d7y.io/dragonfly/v2/pkg/source/httpprotocol"           // Register http client
+	_ "d7y.io/dragonfly/v2/pkg/source/ossprotocol"            // Register oss client
 
-	// Register http client
-	_ "d7y.io/dragonfly/v2/pkg/source/httpprotocol"
-
-	// Register oss client
-	_ "d7y.io/dragonfly/v2/pkg/source/ossprotocol"
+	"d7y.io/dragonfly/v2/cmd/cdn/cmd" //nolint:gci
 )
 
 func main() {
diff --git a/internal/idgen/task_id.go b/internal/idgen/task_id.go
index 8b117029..3287ec83 100644
--- a/internal/idgen/task_id.go
+++ b/internal/idgen/task_id.go
@@ -27,15 +27,13 @@ import (
 // TaskID generates a taskId.
 // filter is separated by & character.
 func TaskID(url string, meta *base.UrlMeta) string {
-	var data []string
-
-	filter := ""
-	if meta != nil {
-		filter = meta.Filter
+	var filters []string
+	if meta != nil && meta.Filter != "" {
+		filters = strings.Split(meta.Filter, "&")
 	}
 
-	data = append(data, urlutils.FilterURLParam(url, strings.Split(filter, "&")))
-
+	var data []string
+	data = append(data, urlutils.FilterURLParam(url, filters))
 	if meta != nil {
 		if meta.Digest != "" {
 			data = append(data, meta.Digest)
diff --git a/pkg/rpc/cdnsystem/server/server.go b/pkg/rpc/cdnsystem/server/server.go
index 4d2dfdc7..bd5898ec 100644
--- a/pkg/rpc/cdnsystem/server/server.go
+++ b/pkg/rpc/cdnsystem/server/server.go
@@ -23,7 +23,6 @@ import (
 
 	"go.uber.org/zap"
 	"google.golang.org/grpc"
-	"google.golang.org/grpc/peer"
 
 	"d7y.io/dragonfly/v2/cdn/metrics"
 	"d7y.io/dragonfly/v2/internal/dferrors"
@@ -63,12 +62,6 @@ func (p *proxy) ObtainSeeds(sr *cdnsystem.SeedRequest, stream cdnsystem.Seeder_O
 	ctx, cancel := context.WithCancel(stream.Context())
 	defer cancel()
 
-	peerAddr := "unknown"
-	if pe, ok := peer.FromContext(ctx); ok {
-		peerAddr = pe.Addr.String()
-	}
-	logger.Infof("trigger obtain seed for taskID: %s, url: %s, from: %s", sr.TaskId, sr.Url, peerAddr)
-
 	errChan := make(chan error, 10)
 	psc := make(chan *cdnsystem.PieceSeed, 4)
 
diff --git a/pkg/util/maputils/map_utils.go b/pkg/util/maputils/map_utils.go
deleted file mode 100644
index 4836b588..00000000
--- a/pkg/util/maputils/map_utils.go
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- *     Copyright 2020 The Dragonfly Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package gc
-
-type Config struct {
-}
-
-func (config Config) applyDefaults() Config {
-	return config
-}
diff --git a/pkg/util/net/urlutils/url_utils.go b/pkg/util/net/urlutils/url_utils.go
index 4d358d57..44b35dc2 100644
--- a/pkg/util/net/urlutils/url_utils.go
+++ b/pkg/util/net/urlutils/url_utils.go
@@ -41,24 +41,11 @@ func FilterURLParam(str string, filters []string) string {
 	}
 
 	u.RawQuery = values.Encode()
-
 	return u.String()
 }
 
 // IsValidURL returns whether the string url is a valid URL.
 func IsValidURL(str string) bool {
 	u, err := url.Parse(str)
-	if err != nil {
-		return false
-	}
-
-	if stringutils.IsBlank(u.Scheme) {
-		return false
-	}
-
-	if stringutils.IsBlank(u.Host) {
-		return false
-	}
-
-	return true
+	return err == nil && u.Scheme != "" && u.Host != ""
 }
diff --git a/scheduler/core/service.go b/scheduler/core/service.go
index 4f5aaae8..8f1c6666 100644
--- a/scheduler/core/service.go
+++ b/scheduler/core/service.go
@@ -219,7 +219,7 @@ func (s *SchedulerService) GetPeer(id string) (*supervisor.Peer, bool) {
 	return s.peerManager.Get(id)
 }
 
-func (s *SchedulerService) RegisterPeerTask(req *schedulerRPC.PeerTaskRequest, task *supervisor.Task) *supervisor.Peer {
+func (s *SchedulerService) RegisterTask(req *schedulerRPC.PeerTaskRequest, task *supervisor.Task) *supervisor.Peer {
 	// get or create host
 	peerHost := req.PeerHost
 	host, ok := s.hostManager.Get(peerHost.Uuid)
@@ -246,10 +246,10 @@ func (s *SchedulerService) RegisterPeerTask(req *schedulerRPC.PeerTaskRequest, t
 	return peer
 }
 
-func (s *SchedulerService) GetOrCreateTask(ctx context.Context, task *supervisor.Task) *supervisor.Task {
+func (s *SchedulerService) GetOrAddTask(ctx context.Context, task *supervisor.Task) *supervisor.Task {
 	span := trace.SpanFromContext(ctx)
-	synclock.Lock(task.ID, true)
 
+	synclock.Lock(task.ID, true)
 	task, ok := s.taskManager.GetOrAdd(task)
 	if ok {
 		span.SetAttributes(config.AttributeTaskStatus.String(task.GetStatus().String()))
@@ -262,6 +262,7 @@ func (s *SchedulerService) GetOrCreateTask(ctx context.Context, task *supervisor
 	} else {
 		task.Log().Infof("add new task %s", task.ID)
 	}
+
 	synclock.UnLock(task.ID, true)
 
 	synclock.Lock(task.ID, false)
diff --git a/scheduler/rpcserver/rpcserver.go b/scheduler/rpcserver/rpcserver.go
index 8e717ccb..712ff8f7 100644
--- a/scheduler/rpcserver/rpcserver.go
+++ b/scheduler/rpcserver/rpcserver.go
@@ -53,66 +53,101 @@ func New(service *core.SchedulerService, opts ...grpc.ServerOption) (*grpc.Serve
 	return svr.Server, nil
 }
 
-func (s *server) RegisterPeerTask(ctx context.Context, request *scheduler.PeerTaskRequest) (resp *scheduler.RegisterResult, err error) {
-	defer func() {
-		logger.Debugf("peer %s register result %v, err: %v", request.PeerId, resp, err)
-	}()
+func (s *server) RegisterPeerTask(ctx context.Context, req *scheduler.PeerTaskRequest) (*scheduler.RegisterResult, error) {
+	taskID := idgen.TaskID(req.Url, req.UrlMeta)
+	log := logger.WithTaskAndPeerID(taskID, req.PeerId)
+	log.Infof("register peer task, req: %#v", req)
+
 	var span trace.Span
 	ctx, span = tracer.Start(ctx, config.SpanPeerRegister, trace.WithSpanKind(trace.SpanKindServer))
-	span.SetAttributes(config.AttributePeerRegisterRequest.String(request.String()))
 	defer span.End()
-	logger.Debugf("register peer task, req: %#v", request)
-	resp = new(scheduler.RegisterResult)
-
-	taskID := idgen.TaskID(request.Url, request.UrlMeta)
+	span.SetAttributes(config.AttributePeerRegisterRequest.String(req.String()))
 	span.SetAttributes(config.AttributeTaskID.String(taskID))
-	task := s.service.GetOrCreateTask(ctx, supervisor.NewTask(taskID, request.Url, request.UrlMeta))
+
+	// Get task or add new task
+	task := s.service.GetOrAddTask(ctx, supervisor.NewTask(taskID, req.Url, req.UrlMeta))
 	if task.IsFail() {
-		err = dferrors.New(base.Code_SchedTaskStatusError, "task status is fail")
-		logger.Errorf("task %s status is fail", task.ID)
-		span.RecordError(err)
-		return
+		dferr := dferrors.New(base.Code_SchedTaskStatusError, "task status is fail")
+		log.Error(dferr.Message)
+		span.RecordError(dferr)
+		return nil, dferr
 	}
-	resp.SizeScope = getTaskSizeScope(task)
-	span.SetAttributes(config.AttributeTaskSizeScope.String(resp.SizeScope.String()))
-	resp.TaskId = taskID
-	switch resp.SizeScope {
-	case base.SizeScope_TINY:
-		resp.DirectPiece = &scheduler.RegisterResult_PieceContent{
-			PieceContent: task.DirectPiece,
-		}
-		return
-	case base.SizeScope_SMALL:
-		peer := s.service.RegisterPeerTask(request, task)
-		parent, schErr := s.service.SelectParent(peer)
-		if schErr != nil {
-			span.AddEvent(config.EventSmallTaskSelectParentFail)
-			resp.SizeScope = base.SizeScope_NORMAL
-			resp.TaskId = taskID
-			return
-		}
-		firstPiece, _ := task.GetPiece(0)
-		singlePiece := &scheduler.SinglePiece{
-			DstPid:  parent.ID,
-			DstAddr: fmt.Sprintf("%s:%d", parent.Host.IP, parent.Host.DownloadPort),
-			PieceInfo: &base.PieceInfo{
-				PieceNum:    firstPiece.PieceNum,
-				RangeStart:  firstPiece.RangeStart,
-				RangeSize:   firstPiece.RangeSize,
-				PieceMd5:    firstPiece.PieceMd5,
-				PieceOffset: firstPiece.PieceOffset,
-				PieceStyle:  firstPiece.PieceStyle,
-			},
-		}
-		resp.DirectPiece = &scheduler.RegisterResult_SinglePiece{
-			SinglePiece: singlePiece,
+
+	// Task has been successful
+	if task.IsSuccess() {
+		log.Info("task has been successful")
+		sizeScope := task.GetSizeScope()
+		span.SetAttributes(config.AttributeTaskSizeScope.String(sizeScope.String()))
+		switch sizeScope {
+		case base.SizeScope_TINY:
+			log.Info("task size scope is tiny and return piece content directly")
+			return &scheduler.RegisterResult{
+				TaskId:    taskID,
+				SizeScope: sizeScope,
+				DirectPiece: &scheduler.RegisterResult_PieceContent{
+					PieceContent: task.DirectPiece,
+				},
+			}, nil
+		case base.SizeScope_SMALL:
+			log.Info("task size scope is small")
+			peer := s.service.RegisterTask(req, task)
+			parent, err := s.service.SelectParent(peer)
+			if err != nil {
+				log.Warn("task size scope is small and it can not select parent")
+				span.AddEvent(config.EventSmallTaskSelectParentFail)
+				return &scheduler.RegisterResult{
+					TaskId:    taskID,
+					SizeScope: sizeScope,
+				}, nil
+			}
+
+			firstPiece, ok := task.GetPiece(0)
+			if !ok {
+				log.Warn("task size scope is small and it can not get first piece")
+				return &scheduler.RegisterResult{
+					TaskId:    taskID,
+					SizeScope: sizeScope,
+				}, nil
+			}
+
+			singlePiece := &scheduler.SinglePiece{
+				DstPid:  parent.ID,
+				DstAddr: fmt.Sprintf("%s:%d", parent.Host.IP, parent.Host.DownloadPort),
+				PieceInfo: &base.PieceInfo{
+					PieceNum:    firstPiece.PieceNum,
+					RangeStart:  firstPiece.RangeStart,
+					RangeSize:   firstPiece.RangeSize,
+					PieceMd5:    firstPiece.PieceMd5,
+					PieceOffset: firstPiece.PieceOffset,
+					PieceStyle:  firstPiece.PieceStyle,
+				},
+			}
+			log.Infof("task size scope is small and return single piece %#v", sizeScope)
+			span.SetAttributes(config.AttributeSinglePiece.String(singlePiece.String()))
+			return &scheduler.RegisterResult{
+				TaskId:    taskID,
+				SizeScope: sizeScope,
+				DirectPiece: &scheduler.RegisterResult_SinglePiece{
+					SinglePiece: singlePiece,
+				},
+			}, nil
+		default:
+			log.Info("task size scope is normal and needs to be register")
+			s.service.RegisterTask(req, task)
+			return &scheduler.RegisterResult{
+				TaskId:    taskID,
+				SizeScope: sizeScope,
+			}, nil
 		}
-		span.SetAttributes(config.AttributeSinglePiece.String(singlePiece.String()))
-		return
-	default:
-		s.service.RegisterPeerTask(request, task)
-		return
 	}
+
+	// Task is unsuccessful
+	log.Info("task is unsuccessful and needs to be register")
+	s.service.RegisterTask(req, task)
+	return &scheduler.RegisterResult{
+		TaskId:    taskID,
+		SizeScope: base.SizeScope_NORMAL,
+	}, nil
 }
 
 func (s *server) ReportPieceResult(stream scheduler.Scheduler_ReportPieceResultServer) error {
@@ -206,15 +241,3 @@ func (s *server) LeaveTask(ctx context.Context, target *scheduler.PeerTarget) (e
 	}
 	return s.service.HandleLeaveTask(ctx, peer)
 }
-
-func getTaskSizeScope(task *supervisor.Task) base.SizeScope {
-	if task.IsSuccess() {
-		if task.ContentLength.Load() <= supervisor.TinyFileSize {
-			return base.SizeScope_TINY
-		}
-		if task.TotalPieceCount.Load() == 1 {
-			return base.SizeScope_SMALL
-		}
-	}
-	return base.SizeScope_NORMAL
-}
diff --git a/scheduler/supervisor/task.go b/scheduler/supervisor/task.go
index 3c53b90f..9ce5cc4b 100644
--- a/scheduler/supervisor/task.go
+++ b/scheduler/supervisor/task.go
@@ -292,6 +292,18 @@ func (task *Task) GetOrAddPiece(p *base.PieceInfo) (*base.PieceInfo, bool) {
 	return piece.(*base.PieceInfo), ok
 }
 
+func (task *Task) GetSizeScope() base.SizeScope {
+	if task.ContentLength.Load() <= TinyFileSize {
+		return base.SizeScope_TINY
+	}
+
+	if task.TotalPieceCount.Load() == 1 {
+		return base.SizeScope_SMALL
+	}
+
+	return base.SizeScope_NORMAL
+}
+
 func (task *Task) CanBackToSource() bool {
 	return task.BackToSourceWeight.Load() > 0
 }


Test patch:
diff --git a/cdn/config/config_test.go b/cdn/config/config_test.go
new file mode 100644
index 00000000..3e6db047
--- /dev/null
+++ b/cdn/config/config_test.go
@@ -0,0 +1,170 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package config
+
+import (
+	"path/filepath"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+
+	"d7y.io/dragonfly/v2/cdn/metrics"
+	"d7y.io/dragonfly/v2/cdn/plugins"
+	"d7y.io/dragonfly/v2/cdn/rpcserver"
+	"d7y.io/dragonfly/v2/cdn/storedriver"
+	"d7y.io/dragonfly/v2/cdn/supervisor/cdn"
+	"d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage"
+	"d7y.io/dragonfly/v2/cdn/supervisor/task"
+	"d7y.io/dragonfly/v2/cmd/dependency/base"
+	"d7y.io/dragonfly/v2/pkg/basic"
+	"d7y.io/dragonfly/v2/pkg/unit"
+)
+
+func TestConfig_Convert(t *testing.T) {
+	dcfg := &DeprecatedConfig{
+		Options: base.Options{
+			Console:   true,
+			Verbose:   true,
+			PProfPort: 1000,
+			Telemetry: base.TelemetryOption{
+				Jaeger:      "https://jaeger.com",
+				ServiceName: "dragonfly-config-test",
+			},
+		},
+		BaseProperties: &BaseProperties{
+			ListenPort:              8006,
+			DownloadPort:            8000,
+			SystemReservedBandwidth: 200 * unit.MB,
+			MaxBandwidth:            20 * unit.GB,
+			AdvertiseIP:             "127.0.0.1",
+			FailAccessInterval:      30 * time.Minute,
+			GCInitialDelay:          60 * time.Second,
+			GCMetaInterval:          20 * time.Minute,
+			TaskExpireTime:          40 * time.Minute,
+			StorageMode:             "disk",
+			LogDir:                  "aaa",
+			WorkHome:                "/workHome",
+			Manager: ManagerConfig{
+				Addr:         "127.0.0.1:8004",
+				CDNClusterID: 5,
+				KeepAlive: KeepAliveConfig{
+					Interval: 50 * time.Second,
+				},
+			},
+			Host: HostConfig{
+				Location: "beijing",
+				IDC:      "na61",
+			},
+			Metrics: &RestConfig{
+				Addr: ":8081",
+			},
+		},
+		Plugins: map[plugins.PluginType][]*plugins.PluginProperties{
+			plugins.StorageDriverPlugin: {
+				{
+					Name:   "disk",
+					Enable: true,
+					Config: &storedriver.Config{
+						BaseDir: filepath.Join(basic.HomeDir, "ftp"),
+					},
+				},
+			}, plugins.StorageManagerPlugin: {
+				{
+					Name:   "disk",
+					Enable: true,
+					Config: &StorageConfig{
+						GCInitialDelay: 10 * time.Second,
+						GCInterval:     150 * time.Second,
+						DriverConfigs: map[string]*DriverConfig{
+							"disk": {
+								GCConfig: &GCConfig{
+									YoungGCThreshold:  1000 * unit.GB,
+									FullGCThreshold:   50 * unit.GB,
+									CleanRatio:        3,
+									IntervalThreshold: 3 * time.Hour,
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+	cfg := dcfg.Convert()
+
+	assert.EqualValues(t, &Config{
+		Options: base.Options{
+			Console:   true,
+			Verbose:   true,
+			PProfPort: 1000,
+			Telemetry: base.TelemetryOption{
+				Jaeger:      "https://jaeger.com",
+				ServiceName: "dragonfly-config-test",
+			},
+		},
+		Metrics: metrics.Config{
+			Net:  "tcp",
+			Addr: ":8081",
+		},
+		Storage: storage.Config{
+			StorageMode:    "disk",
+			GCInitialDelay: 10 * time.Second,
+			GCInterval:     150 * time.Second,
+			DriverConfigs: map[string]*storage.DriverConfig{
+				"disk": {
+					BaseDir: filepath.Join(basic.HomeDir, "ftp"),
+					DriverGCConfig: &storage.DriverGCConfig{
+						YoungGCThreshold:  1000 * unit.GB,
+						FullGCThreshold:   50 * unit.GB,
+						CleanRatio:        3,
+						IntervalThreshold: 3 * time.Hour,
+					},
+				},
+			},
+		},
+		RPCServer: rpcserver.Config{
+			AdvertiseIP:  "127.0.0.1",
+			ListenPort:   8006,
+			DownloadPort: 8000,
+		},
+		Task: task.Config{
+			FailAccessInterval: 30 * time.Minute,
+			GCInitialDelay:     60 * time.Second,
+			GCMetaInterval:     20 * time.Minute,
+			ExpireTime:         40 * time.Minute,
+		},
+		CDN: cdn.Config{
+			SystemReservedBandwidth: 200 * unit.MB,
+			MaxBandwidth:            20 * unit.GB,
+			WriterRoutineLimit:      4,
+		},
+		Manager: ManagerConfig{
+			Addr:         "127.0.0.1:8004",
+			CDNClusterID: 5,
+			KeepAlive: KeepAliveConfig{
+				Interval: 50 * time.Second,
+			},
+		},
+		Host: HostConfig{
+			Location: "beijing",
+			IDC:      "na61",
+		},
+		LogDir:   "aaa",
+		WorkHome: "/workHome",
+	}, cfg)
+}
diff --git a/cdn/storedriver/store_mgr_test.go b/cdn/storedriver/driver_test.go
similarity index 95%
rename from cdn/storedriver/store_mgr_test.go
rename to cdn/storedriver/driver_test.go
index 1430f80c..9a590632 100644
--- a/cdn/storedriver/store_mgr_test.go
+++ b/cdn/storedriver/driver_test.go
@@ -102,9 +102,9 @@ func TestRegister(t *testing.T) {
 	var pluginName = "mock"
 	var baseDir = "/tmp/drivertest"
 	var driverBuilder = newDriver
-	err := Register(pluginName, driverBuilder)
+	err := RegisterPlugin(pluginName, driverBuilder)
 	assert.Nil(err)
-	driver, ok := Get(pluginName)
+	driver, ok := GetPlugin(pluginName)
 	assert.Nil(driver)
 	assert.Equal(false, ok)
 	// enable driver
@@ -120,7 +120,7 @@ func TestRegister(t *testing.T) {
 		},
 	})
 	assert.Nil(err)
-	driver, ok = Get("mock")
+	driver, ok = GetPlugin("mock")
 	assert.Equal(&mockDriver{
 		BaseDir: baseDir,
 	}, driver)
diff --git a/cdn/storedriver/local/local_driver_test.go b/cdn/storedriver/local/local_driver_test.go
index add69283..abfc91aa 100644
--- a/cdn/storedriver/local/local_driver_test.go
+++ b/cdn/storedriver/local/local_driver_test.go
@@ -55,7 +55,7 @@ func (s *LocalDriverTestSuite) SetupSuite() {
 		},
 	}
 	s.Nil(plugins.Initialize(pluginProps))
-	gotDriver, ok := storedriver.Get(DiskDriverName)
+	gotDriver, ok := storedriver.GetPlugin(DiskDriverName)
 	s.True(ok)
 	s.Equal(&driver{BaseDir: filepath.Join(s.workHome, "repo")}, gotDriver)
 	s.Driver = gotDriver
diff --git a/cdn/supervisor/cdn/cache_writer_test.go b/cdn/supervisor/cdn/cache_writer_test.go
index 13b54d5c..b3c96fe0 100644
--- a/cdn/supervisor/cdn/cache_writer_test.go
+++ b/cdn/supervisor/cdn/cache_writer_test.go
@@ -23,22 +23,18 @@ import (
 	"os"
 	"strings"
 	"testing"
-	"time"
 
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/suite"
 
-	"d7y.io/dragonfly/v2/cdn/config"
-	"d7y.io/dragonfly/v2/cdn/constants"
 	"d7y.io/dragonfly/v2/cdn/plugins"
 	"d7y.io/dragonfly/v2/cdn/storedriver"
 	"d7y.io/dragonfly/v2/cdn/storedriver/local"
 	"d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage"
-	"d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage/disk"
 	progressMock "d7y.io/dragonfly/v2/cdn/supervisor/mocks/progress"
+	taskMock "d7y.io/dragonfly/v2/cdn/supervisor/mocks/task"
 	"d7y.io/dragonfly/v2/cdn/supervisor/task"
 	"d7y.io/dragonfly/v2/pkg/ratelimiter/limitreader"
-	"d7y.io/dragonfly/v2/pkg/unit"
 )
 
 func TestCacheWriterSuite(t *testing.T) {
@@ -53,7 +49,7 @@ type CacheWriterTestSuite struct {
 
 func NewPlugins(workHome string) map[plugins.PluginType][]*plugins.PluginProperties {
 	return map[plugins.PluginType][]*plugins.PluginProperties{
-		plugins.StorageDriverPlugin: {
+		"storagedriver": {
 			{
 				Name:   local.DiskDriverName,
 				Enable: true,
@@ -61,24 +57,6 @@ func NewPlugins(workHome string) map[plugins.PluginType][]*plugins.PluginPropert
 					BaseDir: workHome,
 				},
 			},
-		}, plugins.StorageManagerPlugin: {
-			{
-				Name:   disk.StorageMode,
-				Enable: true,
-				Config: &config.StorageConfig{
-					GCInitialDelay: 0 * time.Second,
-					GCInterval:     15 * time.Second,
-					DriverConfigs: map[string]*config.DriverConfig{
-						local.DiskDriverName: {
-							GCConfig: &config.GCConfig{
-								YoungGCThreshold:  100 * unit.GB,
-								FullGCThreshold:   5 * unit.GB,
-								CleanRatio:        1,
-								IntervalThreshold: 2 * time.Hour,
-							}},
-					},
-				},
-			},
 		},
 	}
 }
@@ -91,13 +69,12 @@ func (suite *CacheWriterTestSuite) SetupSuite() {
 	progressManager := progressMock.NewMockManager(ctrl)
 	progressManager.EXPECT().PublishPiece(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
 	progressManager.EXPECT().PublishTask(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
-	storeManager, ok := storage.Get(constants.DefaultStorageMode)
-	if !ok {
-		suite.Failf("failed to get storage mode %s", constants.DefaultStorageMode)
-	}
-	metadataManager := newMetadataManager(storeManager)
+	taskManager := taskMock.NewMockManager(ctrl)
+	storageManager, err := storage.NewManager(storage.DefaultConfig(), taskManager)
+	suite.Require().Nil(err)
+	metadataManager := newMetadataManager(storageManager)
 	cdnReporter := newReporter(progressManager)
-	suite.writer = newCacheWriter(cdnReporter, metadataManager, storeManager)
+	suite.writer = newCacheWriter(cdnReporter, metadataManager, storageManager)
 }
 
 func (suite *CacheWriterTestSuite) TearDownSuite() {
@@ -113,9 +90,10 @@ func (suite *CacheWriterTestSuite) TestStartWriter() {
 	suite.Nil(err)
 	contentLen := int64(len(content))
 	type args struct {
-		reader     *limitreader.LimitReader
-		task       *task.SeedTask
-		breakPoint int64
+		reader             *limitreader.LimitReader
+		task               *task.SeedTask
+		breakPoint         int64
+		writerRoutineLimit int
 	}
 
 	tests := []struct {
@@ -132,6 +110,7 @@ func (suite *CacheWriterTestSuite) TestStartWriter() {
 					ID:        "5806501c3bb92f0b645918c5a4b15495a63259e3e0363008f97e186509e9e",
 					PieceSize: 50,
 				},
+				writerRoutineLimit: 4,
 			},
 			result: &downloadMetadata{
 				backSourceLength:     contentLen,
@@ -148,6 +127,7 @@ func (suite *CacheWriterTestSuite) TestStartWriter() {
 					ID:        "5816501c3bb92f0b645918c5a4b15495a63259e3e0363008f97e186509e9e",
 					PieceSize: 50,
 				},
+				writerRoutineLimit: 4,
 			},
 			result: &downloadMetadata{
 				backSourceLength:     contentLen,
@@ -165,6 +145,7 @@ func (suite *CacheWriterTestSuite) TestStartWriter() {
 					PieceSize:        50,
 					SourceFileLength: contentLen,
 				},
+				writerRoutineLimit: 4,
 			},
 			result: &downloadMetadata{
 				backSourceLength:     contentLen,
@@ -177,7 +158,7 @@ func (suite *CacheWriterTestSuite) TestStartWriter() {
 	}
 	for _, tt := range tests {
 		suite.Run(tt.name, func() {
-			downloadMetadata, err := suite.writer.startWriter(context.Background(), tt.args.reader, tt.args.task, tt.args.breakPoint)
+			downloadMetadata, err := suite.writer.startWriter(context.Background(), tt.args.reader, tt.args.task, tt.args.breakPoint, tt.args.writerRoutineLimit)
 			suite.Equal(tt.wantErr, err != nil)
 			suite.Equal(tt.result, downloadMetadata)
 			suite.checkFileSize(suite.writer.cacheStore, tt.args.task.ID, contentLen)
@@ -195,6 +176,7 @@ func (suite *CacheWriterTestSuite) TestCalculateRoutineCount() {
 	type args struct {
 		remainingFileLength int64
 		pieceSize           int32
+		writerRoutineLimit  int
 	}
 	tests := []struct {
 		name string
@@ -206,6 +188,7 @@ func (suite *CacheWriterTestSuite) TestCalculateRoutineCount() {
 			args: args{
 				remainingFileLength: 200,
 				pieceSize:           50,
+				writerRoutineLimit:  4,
 			},
 			want: 4,
 		},
@@ -214,6 +197,7 @@ func (suite *CacheWriterTestSuite) TestCalculateRoutineCount() {
 			args: args{
 				remainingFileLength: 2222,
 				pieceSize:           50,
+				writerRoutineLimit:  4,
 			},
 			want: 4,
 		},
@@ -222,6 +206,7 @@ func (suite *CacheWriterTestSuite) TestCalculateRoutineCount() {
 			args: args{
 				remainingFileLength: 0,
 				pieceSize:           50,
+				writerRoutineLimit:  4,
 			},
 			want: 1,
 		},
@@ -230,6 +215,7 @@ func (suite *CacheWriterTestSuite) TestCalculateRoutineCount() {
 			args: args{
 				remainingFileLength: 10,
 				pieceSize:           50,
+				writerRoutineLimit:  4,
 			},
 			want: 1,
 		},
@@ -238,13 +224,14 @@ func (suite *CacheWriterTestSuite) TestCalculateRoutineCount() {
 			args: args{
 				remainingFileLength: 10,
 				pieceSize:           0,
+				writerRoutineLimit:  4,
 			},
 			want: 4,
 		},
 	}
 	for _, tt := range tests {
 		suite.Run(tt.name, func() {
-			if got := calculateRoutineCount(tt.args.remainingFileLength, tt.args.pieceSize); got != tt.want {
+			if got := calculateRoutineCount(tt.args.remainingFileLength, tt.args.pieceSize, tt.args.writerRoutineLimit); got != tt.want {
 				suite.Equal(tt.want, got)
 			}
 		})
diff --git a/cdn/supervisor/cdn/manager_test.go b/cdn/supervisor/cdn/manager_test.go
index 6042c5b1..70ee9113 100644
--- a/cdn/supervisor/cdn/manager_test.go
+++ b/cdn/supervisor/cdn/manager_test.go
@@ -27,8 +27,6 @@ import (
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/suite"
 
-	"d7y.io/dragonfly/v2/cdn/config"
-	"d7y.io/dragonfly/v2/cdn/constants"
 	"d7y.io/dragonfly/v2/cdn/plugins"
 	"d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage"
 	_ "d7y.io/dragonfly/v2/cdn/supervisor/cdn/storage/disk"
@@ -59,18 +57,17 @@ func (suite *CDNManagerTestSuite) SetupSuite() {
 	suite.workHome, _ = os.MkdirTemp("/tmp", "cdn-ManagerTestSuite-")
 	fmt.Printf("workHome: %s", suite.workHome)
 	suite.Nil(plugins.Initialize(NewPlugins(suite.workHome)))
-	storeMgr, ok := storage.Get(constants.DefaultStorageMode)
-	if !ok {
-		suite.Failf("failed to get storage mode %s", constants.DefaultStorageMode)
-	}
 	ctrl := gomock.NewController(suite.T())
 	taskManager := taskMock.NewMockManager(ctrl)
+	storageManager, err := storage.NewManager(storage.DefaultConfig(), taskManager)
+	suite.Require().Nil(err)
+
 	progressManager := progressMock.NewMockManager(ctrl)
 	progressManager.EXPECT().PublishPiece(gomock.Any(), md5TaskID, gomock.Any()).Return(nil).Times(98 * 2)
 	progressManager.EXPECT().PublishPiece(gomock.Any(), sha256TaskID, gomock.Any()).Return(nil).Times(98 * 2)
 	progressManager.EXPECT().PublishTask(gomock.Any(), md5TaskID, gomock.Any()).Return(nil).Times(2)
 	progressManager.EXPECT().PublishTask(gomock.Any(), sha256TaskID, gomock.Any()).Return(nil).Times(2)
-	suite.cm, _ = newManager(config.New(), storeMgr, progressManager, taskManager)
+	suite.cm, _ = NewManager(Config{}.applyDefaults(), storageManager, progressManager, taskManager)
 }
 
 var (
diff --git a/cdn/supervisor/cdn/storage/disk/disk_test.go b/cdn/supervisor/cdn/storage/disk/disk_test.go
index 6ec899f7..be7faaf2 100644
--- a/cdn/supervisor/cdn/storage/disk/disk_test.go
+++ b/cdn/supervisor/cdn/storage/disk/disk_test.go
@@ -29,16 +29,16 @@ import (
 	"d7y.io/dragonfly/v2/pkg/unit"
 )
 
-func TestDiskStorageMgrSuite(t *testing.T) {
-	suite.Run(t, new(DiskStorageMgrSuite))
+func TestDiskStorageManagerSuite(t *testing.T) {
+	suite.Run(t, new(DiskStorageManagerSuite))
 }
 
-type DiskStorageMgrSuite struct {
+type DiskStorageManagerSuite struct {
 	m *diskStorageManager
 	suite.Suite
 }
 
-func (suite *DiskStorageMgrSuite) TestTryFreeSpace() {
+func (suite *DiskStorageManagerSuite) TestTryFreeSpace() {
 	ctrl := gomock.NewController(suite.T())
 	diskDriver := storedriver.NewMockDriver(ctrl)
 	taskManager := taskMock.NewMockManager(ctrl)
@@ -47,8 +47,12 @@ func (suite *DiskStorageMgrSuite) TestTryFreeSpace() {
 		taskManager: taskManager,
 	}
 	diskDriver.EXPECT().GetTotalSpace().Return(100*unit.GB, nil)
-	cleaner, _ := storage.NewStorageCleaner(suite.m.getDefaultGcConfig(), diskDriver, suite.m, taskManager)
-	suite.m.cleaner = cleaner
+	diskDriver.EXPECT().GetBaseDir().Return(storage.DefaultDiskBaseDir)
+	driverDriverConfig, err := getDiskDriverConfig(diskDriver)
+	suite.Nil(err)
+	cleaner, err := storage.NewStorageCleaner(driverDriverConfig.DriverGCConfig, diskDriver, suite.m, taskManager)
+	suite.Require().Nil(err)
+	suite.m.diskCleaner = cleaner
 
 	tests := []struct {
 		name       string
diff --git a/cdn/supervisor/progress/manager_test.go b/cdn/supervisor/progress/manager_test.go
index 5eb73af4..77a0df64 100644
--- a/cdn/supervisor/progress/manager_test.go
+++ b/cdn/supervisor/progress/manager_test.go
@@ -24,7 +24,6 @@ import (
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/suite"
 
-	"d7y.io/dragonfly/v2/cdn/config"
 	"d7y.io/dragonfly/v2/cdn/supervisor/task"
 	"d7y.io/dragonfly/v2/pkg/source"
 	"d7y.io/dragonfly/v2/pkg/source/httpprotocol"
@@ -110,7 +109,7 @@ func (suite *ProgressManagerTestSuite) SetupSuite() {
 	source.UnRegister("https")
 	suite.Nil(source.Register("https", sourceClient, httpprotocol.Adapter))
 	sourceClient.EXPECT().GetContentLength(source.RequestEq(testTask.RawURL)).Return(int64(1024*1024*500+1000), nil).Times(1)
-	taskManager, err := task.NewManager(config.New())
+	taskManager, err := task.NewManager(task.Config{})
 	suite.Nil(err)
 	seedTask, err := taskManager.AddOrUpdate(testTask)
 	suite.Nil(err)
diff --git a/cdn/supervisor/task/config_test.go b/cdn/supervisor/task/config_test.go
new file mode 100644
index 00000000..74512d38
--- /dev/null
+++ b/cdn/supervisor/task/config_test.go
@@ -0,0 +1,127 @@
+/*
+ *     Copyright 2020 The Dragonfly Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package task
+
+import (
+	"reflect"
+	"testing"
+	"time"
+)
+
+func TestConfig_applyDefaults(t *testing.T) {
+	type fields struct {
+		GCInitialDelay     time.Duration
+		GCMetaInterval     time.Duration
+		ExpireTime         time.Duration
+		FailAccessInterval time.Duration
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		want   Config
+	}{
+		{
+			name:   "empty config ",
+			fields: fields{},
+			want: Config{
+				GCInitialDelay:     DefaultGCInitialDelay,
+				GCMetaInterval:     DefaultGCMetaInterval,
+				ExpireTime:         DefaultExpireTime,
+				FailAccessInterval: DefaultFailAccessInterval,
+			},
+		}, {
+			name: "unset GCInitialDelay",
+			fields: fields{
+				GCMetaInterval:     2 * time.Second,
+				ExpireTime:         3 * time.Minute,
+				FailAccessInterval: 5 * time.Minute,
+			},
+			want: Config{
+				GCInitialDelay:     DefaultGCInitialDelay,
+				GCMetaInterval:     2 * time.Second,
+				ExpireTime:         3 * time.Minute,
+				FailAccessInterval: 5 * time.Minute,
+			},
+		}, {
+			name: "unset GCMetaInterval",
+			fields: fields{
+				GCInitialDelay:     2 * time.Second,
+				ExpireTime:         3 * time.Minute,
+				FailAccessInterval: 5 * time.Minute,
+			},
+			want: Config{
+				GCInitialDelay:     2 * time.Second,
+				GCMetaInterval:     DefaultGCMetaInterval,
+				ExpireTime:         3 * time.Minute,
+				FailAccessInterval: 5 * time.Minute,
+			},
+		}, {
+			name: "unset TaskExpireTime",
+			fields: fields{
+				GCInitialDelay:     2 * time.Second,
+				GCMetaInterval:     2 * time.Second,
+				FailAccessInterval: 5 * time.Minute,
+			},
+			want: Config{
+				GCInitialDelay:     2 * time.Second,
+				GCMetaInterval:     2 * time.Second,
+				ExpireTime:         DefaultExpireTime,
+				FailAccessInterval: 5 * time.Minute,
+			},
+		}, {
+			name: "unset FailAccessInterval",
+			fields: fields{
+				GCInitialDelay: 2 * time.Second,
+				GCMetaInterval: 2 * time.Second,
+				ExpireTime:     3 * time.Minute,
+			},
+			want: Config{
+				GCInitialDelay:     2 * time.Second,
+				GCMetaInterval:     2 * time.Second,
+				ExpireTime:         3 * time.Minute,
+				FailAccessInterval: DefaultFailAccessInterval,
+			},
+		}, {
+			name: "custom task config",
+			fields: fields{
+				GCInitialDelay:     2 * time.Second,
+				GCMetaInterval:     2 * time.Second,
+				ExpireTime:         3 * time.Minute,
+				FailAccessInterval: 5 * time.Minute,
+			},
+			want: Config{
+				GCInitialDelay:     2 * time.Second,
+				GCMetaInterval:     2 * time.Second,
+				ExpireTime:         3 * time.Minute,
+				FailAccessInterval: 5 * time.Minute,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			c := Config{
+				GCInitialDelay:     tt.fields.GCInitialDelay,
+				GCMetaInterval:     tt.fields.GCMetaInterval,
+				ExpireTime:         tt.fields.ExpireTime,
+				FailAccessInterval: tt.fields.FailAccessInterval,
+			}
+			if got := c.applyDefaults(); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("applyDefaults() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/cdn/supervisor/task/manager_test.go b/cdn/supervisor/task/manager_test.go
index 0468f89c..572a2d69 100644
--- a/cdn/supervisor/task/manager_test.go
+++ b/cdn/supervisor/task/manager_test.go
@@ -26,7 +26,6 @@ import (
 	"github.com/pkg/errors"
 	"github.com/stretchr/testify/require"
 
-	"d7y.io/dragonfly/v2/cdn/config"
 	"d7y.io/dragonfly/v2/internal/util"
 	"d7y.io/dragonfly/v2/pkg/rpc/base"
 	"d7y.io/dragonfly/v2/pkg/source"
@@ -78,7 +77,7 @@ func TestIsTaskNotFound(t *testing.T) {
 
 func Test_manager_Exist(t *testing.T) {
 	httpmock.Activate()
-	tm, err := NewManager(config.New())
+	tm, err := NewManager(Config{})
 	require := require.New(t)
 	require.Nil(err)
 	ctl := gomock.NewController(t)
@@ -99,7 +98,7 @@ func Test_manager_Exist(t *testing.T) {
 }
 
 func Test_manager_AddOrUpdate(t *testing.T) {
-	tm, err := NewManager(config.New())
+	tm, err := NewManager(Config{})
 	require := require.New(t)
 	require.Nil(err)
 	ctl := gomock.NewController(t)