Repository: mosn/layotto (go)

Issue:
Implement Sequencer API component: mysql
**What would you like to be added**:
Add Sequencer API component: mysql #438 

Pull request:
feat: implement sequencer api with mysql
**What this PR does**:
add sequencer api component by mysql

**Which issue(s) this PR fixes**:
Fixes #489 

Patch:
diff --git a/cmd/layotto/main.go b/cmd/layotto/main.go
index 8067c9ff..d91b0e19 100644
--- a/cmd/layotto/main.go
+++ b/cmd/layotto/main.go
@@ -122,6 +122,7 @@ import (
 	sequencer_etcd "mosn.io/layotto/components/sequencer/etcd"
 	sequencer_inmemory "mosn.io/layotto/components/sequencer/in-memory"
 	sequencer_mongo "mosn.io/layotto/components/sequencer/mongo"
+	sequencer_mysql "mosn.io/layotto/components/sequencer/mysql"
 	sequencer_redis "mosn.io/layotto/components/sequencer/redis"
 	sequencer_zookeeper "mosn.io/layotto/components/sequencer/zookeeper"
 
@@ -425,6 +426,9 @@ func NewRuntimeGrpcServer(data json.RawMessage, opts ...grpc.ServerOption) (mgrp
 			runtime_sequencer.NewFactory("in-memory", func() sequencer.Store {
 				return sequencer_inmemory.NewInMemorySequencer()
 			}),
+			runtime_sequencer.NewFactory("mysql", func() sequencer.Store {
+				return sequencer_mysql.NewMySQLSequencer(log.DefaultLogger)
+			}),
 		),
 		// secretstores
 		runtime.WithSecretStoresFactory(
diff --git a/cmd/layotto_multiple_api/main.go b/cmd/layotto_multiple_api/main.go
index 7d1810e7..1f692a24 100644
--- a/cmd/layotto_multiple_api/main.go
+++ b/cmd/layotto_multiple_api/main.go
@@ -130,6 +130,7 @@ import (
 	sequencer_etcd "mosn.io/layotto/components/sequencer/etcd"
 	sequencer_inmemory "mosn.io/layotto/components/sequencer/in-memory"
 	sequencer_mongo "mosn.io/layotto/components/sequencer/mongo"
+	sequencer_mysql "mosn.io/layotto/components/sequencer/mysql"
 	sequencer_redis "mosn.io/layotto/components/sequencer/redis"
 	sequencer_zookeeper "mosn.io/layotto/components/sequencer/zookeeper"
 
@@ -436,6 +437,9 @@ func NewRuntimeGrpcServer(data json.RawMessage, opts ...grpc.ServerOption) (mgrp
 			runtime_sequencer.NewFactory("in-memory", func() sequencer.Store {
 				return sequencer_inmemory.NewInMemorySequencer()
 			}),
+			runtime_sequencer.NewFactory("mysql", func() sequencer.Store {
+				return sequencer_mysql.NewMySQLSequencer(log.DefaultLogger)
+			}),
 		),
 		// secretstores
 		runtime.WithSecretStoresFactory(
diff --git a/components/go.mod b/components/go.mod
index cf51728a..47c08094 100644
--- a/components/go.mod
+++ b/components/go.mod
@@ -3,6 +3,7 @@ module mosn.io/layotto/components
 go 1.14
 
 require (
+	github.com/DATA-DOG/go-sqlmock v1.5.0
 	github.com/alicebob/miniredis/v2 v2.16.0
 	github.com/aliyun/aliyun-oss-go-sdk v2.1.8+incompatible
 	github.com/apache/dubbo-go-hessian2 v1.10.2
diff --git a/components/go.sum b/components/go.sum
index 2c679fe1..d5d2d697 100644
--- a/components/go.sum
+++ b/components/go.sum
@@ -38,6 +38,8 @@ gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0p
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
+github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
 github.com/HdrHistogram/hdrhistogram-go v1.0.1/go.mod h1:BWJ+nMSHY3L41Zj7CA3uXnloDp7xxV0YvstAE7nKTaM=
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
diff --git a/components/pkg/utils/mysql.go b/components/pkg/utils/mysql.go
new file mode 100644
index 00000000..be4b0993
--- /dev/null
+++ b/components/pkg/utils/mysql.go
@@ -0,0 +1,81 @@
+//
+// Copyright 2021 Layotto 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 utils
+
+import (
+	"database/sql"
+	"fmt"
+)
+
+var (
+	Db *sql.DB
+)
+
+const (
+	defaultTableName    = "layotto_sequencer"
+	defaultTableNameKey = "tableName"
+	dataBaseName        = "dataBaseName"
+	userName            = "userName"
+	defaultPassword     = "password"
+	mysqlUrl            = "mysqlUrl"
+)
+
+type MySQLMetadata struct {
+	TableName    string
+	DataBaseName string
+	UserName     string
+	Password     string
+	MysqlUrl     string
+	Db           *sql.DB
+}
+
+func ParseMySQLMetadata(properties map[string]string) (MySQLMetadata, error) {
+	m := MySQLMetadata{}
+
+	if val, ok := properties[defaultTableNameKey]; ok && val != "" {
+		m.TableName = val
+	}
+	if val, ok := properties[dataBaseName]; ok && val != "" {
+		m.DataBaseName = val
+	}
+	if val, ok := properties[userName]; ok && val != "" {
+		m.UserName = val
+	}
+	if val, ok := properties[defaultPassword]; ok && val != "" {
+		m.Password = val
+	}
+	if val, ok := properties[mysqlUrl]; ok && val != "" {
+		m.MysqlUrl = val
+	}
+	return m, nil
+}
+
+func NewMySQLClient(meta MySQLMetadata) error {
+
+	val := meta
+	if val.TableName == "" {
+		val.TableName = defaultTableName
+	}
+	meta.Db.Begin()
+	createTable := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s (
+			sequencer_key VARCHAR(255),
+			sequencer_value INT,
+			UNIQUE INDEX (sequencer_key));`, val.TableName)
+	_, err := meta.Db.Exec(createTable)
+	defer meta.Db.Close()
+	if err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/components/sequencer/mysql/mysql.go b/components/sequencer/mysql/mysql.go
new file mode 100644
index 00000000..fe2502ed
--- /dev/null
+++ b/components/sequencer/mysql/mysql.go
@@ -0,0 +1,162 @@
+//
+// Copyright 2021 Layotto 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 mysql
+
+import (
+	"database/sql"
+	"fmt"
+
+	"mosn.io/pkg/log"
+
+	"mosn.io/layotto/components/pkg/utils"
+	"mosn.io/layotto/components/sequencer"
+)
+
+type MySQLSequencer struct {
+	metadata   utils.MySQLMetadata
+	biggerThan map[string]int64
+	logger     log.ErrorLogger
+	db         *sql.DB
+}
+
+func NewMySQLSequencer(logger log.ErrorLogger) *MySQLSequencer {
+	s := &MySQLSequencer{
+		logger: logger,
+	}
+
+	return s
+}
+
+func (e *MySQLSequencer) Init(config sequencer.Configuration) error {
+
+	m, err := utils.ParseMySQLMetadata(config.Properties)
+
+	if err != nil {
+		return err
+	}
+	e.metadata = m
+	e.metadata.Db = e.db
+	e.biggerThan = config.BiggerThan
+
+	if err = utils.NewMySQLClient(e.metadata); err != nil {
+		return err
+	}
+
+	if len(e.biggerThan) > 0 {
+		var Key string
+		var Value int64
+		for k, bt := range e.biggerThan {
+			if bt > 0 {
+				m.Db.QueryRow(fmt.Sprintf("SELECT sequencer_key,sequencer_value FROM %s WHERE sequencer_key = ?", m.TableName), k).Scan(&Key, &Value)
+				if Value < bt {
+					return fmt.Errorf("mysql sequencer error: can not satisfy biggerThan guarantee.key: %s,key in MySQL: %s", k, Key)
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func (e *MySQLSequencer) GetNextId(req *sequencer.GetNextIdRequest) (*sequencer.GetNextIdResponse, error) {
+
+	metadata, err := utils.ParseMySQLMetadata(req.Metadata)
+	metadata.Db = e.db
+	var Key string
+	var Value int64
+	var Version int64
+	var oldVersion int64
+	if err != nil {
+		return nil, err
+	}
+	begin, err := metadata.Db.Begin()
+	if err != nil {
+		return nil, err
+	}
+	err = begin.QueryRow("SELECT sequencer_key, sequencer_value, version FROM ? WHERE sequencer_key = ?", metadata.TableName, req.Key).Scan(&Key, &Value, &oldVersion)
+
+	if err == sql.ErrNoRows {
+		Value = 1
+		Version = 1
+		_, err := begin.Exec("INSERT INTO ?(sequencer_key, sequencer_value, version) VALUES(?,?,?)", metadata.TableName, req.Key, Value, Version)
+		if err != nil {
+			begin.Rollback()
+			return nil, err
+		}
+
+	} else {
+		_, err := begin.Exec("UPDATE ? SET sequencer_value +=1, version += 1 WHERE sequencer_key = ? and version = ?", metadata.TableName, req.Key, oldVersion)
+		if err != nil {
+			begin.Rollback()
+			return nil, err
+		}
+	}
+	defer e.Close(metadata.Db)
+
+	return &sequencer.GetNextIdResponse{
+		NextId: Value,
+	}, nil
+}
+
+func (e *MySQLSequencer) GetSegment(req *sequencer.GetSegmentRequest) (support bool, result *sequencer.GetSegmentResponse, err error) {
+
+	if req.Size == 0 {
+		return true, nil, nil
+	}
+
+	metadata, err := utils.ParseMySQLMetadata(req.Metadata)
+
+	var Key string
+	var Value int64
+	var Version int64
+	var oldVersion int64
+	if err != nil {
+		return false, nil, err
+	}
+
+	metadata.Db = e.db
+
+	begin, err := metadata.Db.Begin()
+	if err != nil {
+		return false, nil, err
+	}
+	err = begin.QueryRow("SELECT sequencer_key, sequencer_value, version FROM ? WHERE sequencer_key == ?", metadata.TableName, req.Key).Scan(&Key, &Value, &oldVersion)
+	if err == sql.ErrNoRows {
+		Value = int64(req.Size)
+		Version = 1
+		_, err := begin.Exec("INSERT INTO ?(sequencer_key, sequencer_value, version) VALUES(?,?,?)", metadata.TableName, req.Key, Value, Version)
+		if err != nil {
+			begin.Rollback()
+			return false, nil, err
+		}
+
+	} else {
+		Value += int64(req.Size)
+		_, err1 := begin.Exec("UPDATE ? SET sequencer_value = ?, version += 1 WHERE sequencer_key = ? AND version = ?", metadata.TableName, Value, req.Key, oldVersion)
+		if err1 != nil {
+			begin.Rollback()
+			return false, nil, err1
+		}
+	}
+	defer e.Close(metadata.Db)
+
+	return false, &sequencer.GetSegmentResponse{
+		From: Value - int64(req.Size) + 1,
+		To:   Value,
+	}, nil
+}
+
+func (e *MySQLSequencer) Close(db *sql.DB) error {
+
+	return db.Close()
+}
diff --git a/docs/_sidebar.md b/docs/_sidebar.md
index 949219f3..0c81dc3d 100644
--- a/docs/_sidebar.md
+++ b/docs/_sidebar.md
@@ -73,6 +73,7 @@
         - [Redis](en/component_specs/sequencer/redis.md)
         - [Zookeeper](en/component_specs/sequencer/zookeeper.md)
         - [MongoDB](en/component_specs/sequencer/mongo.md)
+        - [Mysql](en/component_specs/sequencer/mysql.md)
       - [Secret Store](en/component_specs/secret/common.md)
   - [How to deploy and upgrade Layotto](en/operation/)
 - Design documents
diff --git a/docs/en/component_specs/sequencer/mysql.md b/docs/en/component_specs/sequencer/mysql.md
new file mode 100644
index 00000000..fd1aa41c
--- /dev/null
+++ b/docs/en/component_specs/sequencer/mysql.md
@@ -0,0 +1,18 @@
+# mysql
+
+## metadata fields
+| Field        | Required  | Description                                                               |
+|-----------|-----|---------------------------------------------------------------------------|
+| mysqlUrl  | Y   | mysql's service address                                                   |
+| username  | N   | specify username                                                          |
+| password  | N   | specify password                                                          |
+| dataBaseName| N   | mysql dataBaseName                                                        |
+| connectionString| Y   | Database connection string such as "root:123456@tcp(127.0.0.1:3306)/test" |
+
+## How to start mysql
+
+If you want to run the mysql demo, you need to start a mysql server with Docker first.
+
+```shell 
+docker run --name mysql-test -d -p 3306:3306 mysql
+```
\ No newline at end of file
diff --git a/docs/zh/_sidebar.md b/docs/zh/_sidebar.md
index e61e5a6c..ccfdedea 100644
--- a/docs/zh/_sidebar.md
+++ b/docs/zh/_sidebar.md
@@ -79,7 +79,8 @@
                 - [Redis](zh/component_specs/sequencer/redis.md)
                 - [Zookeeper](zh/component_specs/sequencer/zookeeper.md)
                 - [MongoDB](zh/component_specs/sequencer/mongo.md)
-            - [Secret Store](zh/component_specs/secret/common.md)
+                - [Mysql](zh/component_specs/sequencer/mysql.md)
+            - [Secret Store](zh/component_specs/secret/common.md)  
             - [自定义组件](zh/component_specs/custom/common.md)
     - [如何部署、升级 Layotto](zh/operation/)
     - [如何本地开发、本地调试](zh/operation/local.md)
diff --git a/docs/zh/component_specs/sequencer/mysql.md b/docs/zh/component_specs/sequencer/mysql.md
new file mode 100644
index 00000000..6e967693
--- /dev/null
+++ b/docs/zh/component_specs/sequencer/mysql.md
@@ -0,0 +1,18 @@
+# mysql
+
+## 配置项说明
+| 字段        | 必填  | 说明         |
+|-----------|-----|------------|
+| mysqlUrl  | Y   | mysql的服务地址 |
+| username  | N   | mysql用户名   |
+ | password  | N   | mysql密码    |
+ | dataBaseName| N   | mysql数据库名称 |
+| connectionString| Y   | 数据库连接串 例如 "root:123456@tcp(127.0.0.1:3306)/test" |
+
+## 怎么启动 mysql
+
+如果想启动mysql的demo，需要先用Docker启动一个mysql命令：
+
+```shell 
+docker run --name mysql-test -d -p 3306:3306 mysql
+```
\ No newline at end of file


Test patch:
diff --git a/components/sequencer/mysql/mysql_test.go b/components/sequencer/mysql/mysql_test.go
new file mode 100644
index 00000000..7c6df65b
--- /dev/null
+++ b/components/sequencer/mysql/mysql_test.go
@@ -0,0 +1,346 @@
+//
+// Copyright 2021 Layotto 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 mysql
+
+import (
+	"database/sql"
+	"database/sql/driver"
+	"errors"
+	"testing"
+
+	"github.com/DATA-DOG/go-sqlmock"
+	"github.com/stretchr/testify/assert"
+	"mosn.io/pkg/log"
+
+	"mosn.io/layotto/components/sequencer"
+)
+
+const (
+	MySQLUrl     = "localhost:3306"
+	Value        = 1
+	Key          = "sequenceKey"
+	Size         = 50
+	Version      = 1
+	tableName    = "layotto_sequencer"
+	dataBaseName = "layotto"
+	userName     = "root"
+	password     = "123456"
+)
+
+func TestMySQLSequencer_Init(t *testing.T) {
+
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+
+	comp := NewMySQLSequencer(log.DefaultLogger)
+	comp.db = db
+
+	cfg := sequencer.Configuration{
+		Properties: make(map[string]string),
+		BiggerThan: make(map[string]int64),
+	}
+	mock.ExpectBegin()
+	mock.ExpectExec("CREATE TABLE IF NOT EXISTS").WillReturnResult(sqlmock.NewResult(1, 1))
+	mock.ExpectCommit()
+
+	cfg.Properties["tableName"] = tableName
+	cfg.Properties["dataBaseName"] = dataBaseName
+	cfg.Properties["userName"] = userName
+	cfg.Properties["password"] = password
+	cfg.Properties["mysqlUrl"] = MySQLUrl
+	err = comp.Init(cfg)
+
+	assert.Nil(t, err)
+}
+
+func TestMySQLSequencer_GetNextId(t *testing.T) {
+
+	comp := NewMySQLSequencer(log.DefaultLogger)
+
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+
+	rows := sqlmock.NewRows([]string{"sequencer_key", "sequencer_value", "version"}).AddRow([]driver.Value{Key, Value, Version}...)
+
+	mock.ExpectBegin()
+	mock.ExpectQuery("SELECT").WillReturnRows(rows)
+	mock.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(1, 1))
+	mock.ExpectExec("INSERT INTO").WithArgs(tableName, Key, Value, Version).WillReturnResult(sqlmock.NewResult(1, 1))
+	mock.ExpectCommit()
+
+	properties := make(map[string]string)
+
+	properties["tableName"] = tableName
+	properties["dataBaseName"] = dataBaseName
+	properties["userName"] = userName
+	properties["password"] = password
+	properties["mysqlUrl"] = MySQLUrl
+
+	req := &sequencer.GetNextIdRequest{Key: Key, Options: sequencer.SequencerOptions{AutoIncrement: sequencer.STRONG}, Metadata: properties}
+
+	comp.db = db
+	_, err = comp.GetNextId(req)
+	if err != nil {
+		t.Errorf("error was not expected while updating stats: %s", err)
+	}
+
+	assert.NoError(t, err)
+}
+
+func TestMySQLSequencer_GetSegment(t *testing.T) {
+
+	comp := NewMySQLSequencer(log.DefaultLogger)
+
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	rows := sqlmock.NewRows([]string{"sequencer_key", "sequencer_value", "version"}).AddRow([]driver.Value{Key, Value, Version}...)
+
+	mock.ExpectBegin()
+	mock.ExpectQuery("SELECT").WillReturnRows(rows)
+	mock.ExpectExec("UPDATE").WillReturnResult(sqlmock.NewResult(1, 1))
+	mock.ExpectExec("INSERT INTO").WithArgs(tableName, Key, Size, Version).WillReturnResult(sqlmock.NewResult(1, 1))
+	mock.ExpectCommit()
+
+	properties := make(map[string]string)
+
+	properties["tableName"] = tableName
+	properties["dataBaseName"] = dataBaseName
+	properties["userName"] = userName
+	properties["password"] = password
+	properties["mysqlUrl"] = MySQLUrl
+
+	req := &sequencer.GetSegmentRequest{Size: Size, Key: Key, Options: sequencer.SequencerOptions{AutoIncrement: sequencer.STRONG}, Metadata: properties}
+
+	comp.db = db
+	_, _, err = comp.GetSegment(req)
+	if err != nil {
+		t.Errorf("error was not expected while updating stats: %s", err)
+	}
+
+	assert.NoError(t, err)
+}
+
+func TestMySQLSequencer_Close(t *testing.T) {
+
+	var MySQLUrl = MySQLUrl
+
+	db, _, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+
+	comp := NewMySQLSequencer(log.DefaultLogger)
+
+	cfg := sequencer.Configuration{
+		BiggerThan: nil,
+		Properties: make(map[string]string),
+	}
+
+	cfg.Properties["MySQLHost"] = MySQLUrl
+
+	comp.db = db
+	_ = comp.Init(cfg)
+
+	comp.Close(db)
+}
+
+func TestMySQLSequencer_Segment_Insert(t *testing.T) {
+	comp := NewMySQLSequencer(log.DefaultLogger)
+
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	mock.ExpectBegin()
+	mock.ExpectQuery("SELECT").WillReturnError(sql.ErrNoRows)
+	mock.ExpectExec("INSERT INTO").WithArgs(tableName, Key, Size, Version).WillReturnResult(sqlmock.NewResult(1, 1))
+	mock.ExpectCommit()
+
+	properties := make(map[string]string)
+
+	properties["tableName"] = tableName
+	properties["dataBaseName"] = dataBaseName
+	properties["userName"] = userName
+	properties["password"] = password
+	properties["mysqlUrl"] = MySQLUrl
+
+	segmentReq := &sequencer.GetSegmentRequest{Size: Size, Key: Key, Options: sequencer.SequencerOptions{AutoIncrement: sequencer.STRONG}, Metadata: properties}
+	comp.db = db
+	_, _, err = comp.GetSegment(segmentReq)
+	if err != nil {
+		t.Errorf("error was not expected while updating stats: %s", err)
+	}
+
+	assert.NoError(t, err)
+}
+
+func TestMySQLSequencer_GetNextId_Insert(t *testing.T) {
+	comp := NewMySQLSequencer(log.DefaultLogger)
+
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+
+	mock.ExpectBegin()
+	mock.ExpectQuery("SELECT").WillReturnError(sql.ErrNoRows)
+	mock.ExpectExec("INSERT INTO").WithArgs(tableName, Key, Value, Version).WillReturnResult(sqlmock.NewResult(1, 1))
+	mock.ExpectCommit()
+
+	properties := make(map[string]string)
+
+	properties["tableName"] = tableName
+	properties["dataBaseName"] = dataBaseName
+	properties["userName"] = userName
+	properties["password"] = password
+	properties["mysqlUrl"] = MySQLUrl
+
+	req := &sequencer.GetNextIdRequest{Key: Key, Options: sequencer.SequencerOptions{AutoIncrement: sequencer.STRONG}, Metadata: properties}
+
+	comp.db = db
+	_, err = comp.GetNextId(req)
+	if err != nil {
+		t.Errorf("error was not expected while updating stats: %s", err)
+	}
+
+	assert.NoError(t, err)
+}
+
+func TestMySQLSequencer_GetNextId_InsertError(t *testing.T) {
+	comp := NewMySQLSequencer(log.DefaultLogger)
+
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+
+	mock.ExpectBegin()
+	mock.ExpectQuery("SELECT").WillReturnError(sql.ErrNoRows)
+	properties := make(map[string]string)
+
+	properties["tableName"] = tableName
+	properties["dataBaseName"] = dataBaseName
+	properties["userName"] = userName
+	properties["password"] = password
+	properties["mysqlUrl"] = MySQLUrl
+
+	req := &sequencer.GetNextIdRequest{Key: Key, Options: sequencer.SequencerOptions{AutoIncrement: sequencer.STRONG}, Metadata: properties}
+
+	comp.db = db
+	_, err = comp.GetNextId(req)
+
+	assert.Error(t, err)
+}
+
+func TestMySQLSequencer_Segment_InsertError(t *testing.T) {
+	comp := NewMySQLSequencer(log.DefaultLogger)
+
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	mock.ExpectBegin()
+	mock.ExpectQuery("SELECT").WillReturnError(sql.ErrNoRows)
+	mock.ExpectExec("INSERT INTO").WithArgs(tableName, Key, Size).WillReturnError(errors.New("insert error"))
+	mock.ExpectCommit()
+
+	properties := make(map[string]string)
+
+	properties["tableName"] = tableName
+	properties["dataBaseName"] = dataBaseName
+	properties["userName"] = userName
+	properties["password"] = password
+	properties["mysqlUrl"] = MySQLUrl
+
+	segmentReq := &sequencer.GetSegmentRequest{Size: Size, Key: Key, Options: sequencer.SequencerOptions{AutoIncrement: sequencer.STRONG}, Metadata: properties}
+	comp.db = db
+	_, _, err = comp.GetSegment(segmentReq)
+
+	assert.Error(t, err)
+}
+
+func TestMySQLSequencer_GetNextId_UpdateError(t *testing.T) {
+	comp := NewMySQLSequencer(log.DefaultLogger)
+
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+
+	rows := sqlmock.NewRows([]string{"sequencer_key", "sequencer_value", "version"}).AddRow([]driver.Value{Key, Value, Version}...)
+
+	mock.ExpectBegin()
+	mock.ExpectQuery("SELECT").WillReturnRows(rows)
+	mock.ExpectExec("UPDATE").WillReturnError(errors.New("update error"))
+	mock.ExpectCommit()
+
+	properties := make(map[string]string)
+
+	properties["tableName"] = tableName
+	properties["dataBaseName"] = dataBaseName
+	properties["userName"] = userName
+	properties["password"] = password
+	properties["mysqlUrl"] = MySQLUrl
+
+	req := &sequencer.GetNextIdRequest{Key: Key, Options: sequencer.SequencerOptions{AutoIncrement: sequencer.STRONG}, Metadata: properties}
+
+	comp.db = db
+	_, err = comp.GetNextId(req)
+
+	assert.Error(t, err)
+}
+
+func TestMySQLSequencer_Segment_UpdateError(t *testing.T) {
+	comp := NewMySQLSequencer(log.DefaultLogger)
+
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	rows := sqlmock.NewRows([]string{"sequencer_key", "sequencer_value", "version"}).AddRow([]driver.Value{Key, Value, Version}...)
+
+	mock.ExpectBegin()
+	mock.ExpectQuery("SELECT").WillReturnRows(rows)
+	mock.ExpectExec("UPDATE").WillReturnError(errors.New("update error"))
+	mock.ExpectCommit()
+
+	properties := make(map[string]string)
+
+	properties["tableName"] = tableName
+	properties["dataBaseName"] = dataBaseName
+	properties["userName"] = userName
+	properties["password"] = password
+	properties["mysqlUrl"] = MySQLUrl
+
+	req := &sequencer.GetSegmentRequest{Size: Size, Key: Key, Options: sequencer.SequencerOptions{AutoIncrement: sequencer.STRONG}, Metadata: properties}
+
+	comp.db = db
+	_, _, err = comp.GetSegment(req)
+
+	assert.Error(t, err)
+}