Skip to content

Commit

Permalink
fix: refactor quota table (#1110)
Browse files Browse the repository at this point in the history
* fix: fix update quota

* fix: fix check quota enough logic

* fix: fix and refactor traffic table and update logic

* fix: fix comment
  • Loading branch information
flywukong authored Sep 6, 2023
1 parent 0595e7e commit 9b4007a
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 58 deletions.
89 changes: 56 additions & 33 deletions store/sqldb/traffic.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,30 +79,39 @@ func (s *SpDBImpl) CheckQuotaAndAddReadRecord(record *corespdb.ReadRecord, quota
return nil
}

func getUpdatedConsumedQuota(record *corespdb.ReadRecord, freeQuota, freeConsumedQuota, totalConsumeQuota, chargedQuota uint64) (uint64, uint64, error) {
recordQuotaCost := record.ReadSize
needCheckChainQuota := true
freeQuotaRemain := freeQuota - freeConsumedQuota
// if remain free quota more than 0, consume free quota first
if freeQuotaRemain > 0 && recordQuotaCost < freeQuotaRemain {
// if free quota is enough, no need to check charged quota
totalConsumeQuota += recordQuotaCost
freeConsumedQuota += recordQuotaCost
needCheckChainQuota = false
}
// if free quota is not enough, check the charged quota
if needCheckChainQuota {
// the quota size of this month should be (chargedQuota + freeQuotaRemain)
if totalConsumeQuota+recordQuotaCost > chargedQuota+freeQuotaRemain {
return 0, 0, ErrCheckQuotaEnough
}
totalConsumeQuota += recordQuotaCost
// getUpdatedConsumedQuota compute the updated quota of traffic table by the incoming read cost and the newest record.
// it returns the updated consumed free quota,consumed charged quota and remained free quota
func getUpdatedConsumedQuota(recordQuotaCost, freeQuotaRemain, consumeFreeQuota, consumeChargedQuota, chargedQuota uint64) (uint64, uint64, uint64, bool, error) {
// if remain free quota enough, just consume free quota
needUpdateFreeQuota := false
if recordQuotaCost < freeQuotaRemain {
needUpdateFreeQuota = true
consumeFreeQuota += recordQuotaCost
freeQuotaRemain -= recordQuotaCost
} else {
// if free remain quota exist, consume all the free remain quota first
if freeQuotaRemain > 0 {
freeConsumedQuota += freeQuotaRemain
if freeQuotaRemain+chargedQuota < recordQuotaCost {
return 0, 0, 0, false, ErrCheckQuotaEnough
}
needUpdateFreeQuota = true
consumeFreeQuota += freeQuotaRemain
// update the consumed charge quota by remained free quota
// if read cost 5G, and the remained free quota is 2G, consumed charge quota should be 3G and remained free quota should be 0
consumeQuota := recordQuotaCost - freeQuotaRemain
consumeChargedQuota += consumeQuota
freeQuotaRemain = uint64(0)
log.CtxDebugw(context.Background(), "free quota has been exhausted", "consumed", consumeFreeQuota, "remained", freeQuotaRemain)
} else {
// free remain quota is zero, no need to consider the free quota
// the consumeChargedQuota plus record cost need to be more than total charged quota
if chargedQuota < consumeChargedQuota+recordQuotaCost {
return 0, 0, 0, false, ErrCheckQuotaEnough
}
consumeChargedQuota += recordQuotaCost
}
}

return freeConsumedQuota, totalConsumeQuota, nil
return consumeFreeQuota, consumeChargedQuota, freeQuotaRemain, needUpdateFreeQuota, nil
}

// updateConsumedQuota update the consumed quota of BucketTraffic table in the transaction way
Expand All @@ -111,7 +120,7 @@ func (s *SpDBImpl) updateConsumedQuota(record *corespdb.ReadRecord, quota *cores
err := s.db.Transaction(func(tx *gorm.DB) error {
var bucketTraffic BucketTrafficTable
var err error
if err = tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("bucket_id = ? and month = ?", record.BucketID, yearMonth).Find(&bucketTraffic).Error; err != nil {
if err = tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("bucket_id = ? and month = ?", record.BucketID, yearMonth).First(&bucketTraffic).Error; err != nil {
return fmt.Errorf("failed to query bucket traffic table: %v", err)
}

Expand All @@ -122,36 +131,50 @@ func (s *SpDBImpl) updateConsumedQuota(record *corespdb.ReadRecord, quota *cores
ChargedQuotaSize: quota.ChargedQuotaSize,
ModifiedTime: time.Now(),
})

if result.Error != nil {
return fmt.Errorf("failed to update bucket traffic table: %s", result.Error)
}

if result.RowsAffected != 1 {
return fmt.Errorf("update traffic of %s has affected more than one rows %d, "+
"update charged quota %d", bucketTraffic.BucketName, result.RowsAffected, quota.ChargedQuotaSize)
}
log.CtxDebugw(context.Background(), "updated quota", "charged quota", quota.ChargedQuotaSize)
}

// compute the new consumed quota size to be updated
updatedReadConsumedSize, updatedFreeConsumedSize, err := getUpdatedConsumedQuota(record,
// compute the new consumed quota size to be updated by the newest record and the read cost size
updatedConsumedFreeQuota, updatedConsumedChargedQuota, updatedRemainedFreeQuota, needUpdateFreeQuota, err := getUpdatedConsumedQuota(record.ReadSize,
bucketTraffic.FreeQuotaSize, bucketTraffic.FreeQuotaConsumedSize,
bucketTraffic.ReadConsumedSize, bucketTraffic.ChargedQuotaSize)
bucketTraffic.ReadConsumedSize, quota.ChargedQuotaSize)
if err != nil {
return err
}

if err = tx.Model(&bucketTraffic).
Updates(BucketTrafficTable{
ReadConsumedSize: updatedReadConsumedSize,
FreeQuotaConsumedSize: updatedFreeConsumedSize,
if needUpdateFreeQuota {
// it is needed to add select items if you need to update a value to zero in gorm db
err = tx.Model(&bucketTraffic).
Select("read_consumed_size", "free_quota_consumed_size", "free_quota_size", "modified_time").Updates(BucketTrafficTable{
ReadConsumedSize: updatedConsumedChargedQuota,
FreeQuotaConsumedSize: updatedConsumedFreeQuota,
FreeQuotaSize: updatedRemainedFreeQuota,
ModifiedTime: time.Now(),
}).Error; err != nil {
}).Error
} else {
err = tx.Model(&bucketTraffic).Updates(BucketTrafficTable{
ReadConsumedSize: updatedConsumedChargedQuota,
ModifiedTime: time.Now(),
}).Error
}
if err != nil {
return fmt.Errorf("failed to update bucket traffic table: %v", err)
}

return nil
})

if err != nil {
log.CtxErrorw(context.Background(), "updated quota transaction fail", "error", err)
}
return err
}

Expand Down Expand Up @@ -194,14 +217,13 @@ func (s *SpDBImpl) InitBucketTraffic(record *corespdb.ReadRecord, quota *corespd
BucketID: bucketID,
Month: yearMonth,
FreeQuotaSize: newestTraffic.FreeQuotaSize,
FreeQuotaConsumedSize: newestTraffic.FreeQuotaConsumedSize,
FreeQuotaConsumedSize: 0,
BucketName: bucketName,
ReadConsumedSize: 0,
ChargedQuotaSize: quota.ChargedQuotaSize,
ModifiedTime: time.Now(),
}
}

result = tx.Create(insertBucketTraffic)
if result.Error != nil && MysqlErrCode(result.Error) != ErrDuplicateEntryCode {
return fmt.Errorf("failed to create bucket traffic table: %s", result.Error)
Expand Down Expand Up @@ -246,6 +268,7 @@ func (s *SpDBImpl) GetBucketTraffic(bucketID uint64, yearMonth string) (traffic
err = fmt.Errorf("failed to query bucket traffic table: %s", result.Error)
return nil, err
}

return &corespdb.BucketTraffic{
BucketID: queryReturn.BucketID,
YearMonth: queryReturn.Month,
Expand Down
8 changes: 4 additions & 4 deletions store/sqldb/traffic_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ type BucketTrafficTable struct {
BucketID uint64 `gorm:"primary_key"`
Month string `gorm:"primary_key"`
BucketName string
ReadConsumedSize uint64
FreeQuotaConsumedSize uint64 // indicates the consumed free quota size
FreeQuotaSize uint64 // the greenfield chain free quota
ChargedQuotaSize uint64 //the greenfield chain bucket charged quota
ReadConsumedSize uint64 // indicates the consumed chargedQuota of this month
FreeQuotaConsumedSize uint64 // indicates the consumed free quota size of this month
FreeQuotaSize uint64 // indicate the remained free quota
ChargedQuotaSize uint64 // indicate the greenfield chain bucket charged quota
ModifiedTime time.Time
}

Expand Down
46 changes: 25 additions & 21 deletions store/sqldb/traffic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestSpDBImpl_CheckQuotaAndAddReadRecordSuccess1(t *testing.T) {
BucketID: 2,
BucketName: "mockBucketName",
ReadConsumedSize: 10,
FreeQuotaConsumedSize: 100,
FreeQuotaConsumedSize: 10,
FreeQuotaSize: 25,
ChargedQuotaSize: 30,
ModifiedTime: time.Now(),
Expand All @@ -52,16 +52,16 @@ func TestSpDBImpl_CheckQuotaAndAddReadRecordSuccess1(t *testing.T) {
yearMonth := TimestampYearMonth(b.ModifiedTime.Unix())
s, mock := setupDB(t)
mock.ExpectBegin()
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? ORDER BY `bucket_traffic`.`bucket_id` LIMIT 1 FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
"free_quota_size", "charged_quota_size", "modified_time"}).AddRow(b.BucketID, yearMonth, b.BucketName, b.ReadConsumedSize,
b.FreeQuotaConsumedSize, b.FreeQuotaSize, b.ChargedQuotaSize, b.ModifiedTime))

mock.ExpectExec("UPDATE `bucket_traffic` SET `charged_quota_size`=?,`modified_time`=? WHERE `bucket_id` = ? ").
WithArgs(20, sqlmock.AnyArg(), record.BucketID).
WillReturnResult(sqlmock.NewResult(1, 1))

mock.ExpectExec("UPDATE `bucket_traffic` SET `read_consumed_size`=?,`free_quota_consumed_size`=?,`modified_time`=? WHERE `bucket_id` = ? ").
WithArgs(sqlmock.AnyArg(), 12, sqlmock.AnyArg(), record.BucketID).
mock.ExpectExec("UPDATE `bucket_traffic` SET `read_consumed_size`=?,`free_quota_consumed_size`=?,`free_quota_size`=?,`modified_time`=? WHERE `bucket_id` = ? ").
WithArgs(sqlmock.AnyArg(), 12, 23, sqlmock.AnyArg(), record.BucketID).
WillReturnResult(sqlmock.NewResult(1, 1))

mock.ExpectCommit()
Expand All @@ -72,7 +72,6 @@ func TestSpDBImpl_CheckQuotaAndAddReadRecordSuccess1(t *testing.T) {
mock.ExpectCommit()

err := s.CheckQuotaAndAddReadRecord(newRecord, quota)

assert.Nil(t, err)
}

Expand Down Expand Up @@ -104,11 +103,12 @@ func TestSpDBImpl_CheckQuotaAndAddReadRecordSuccess2(t *testing.T) {
yearMonth := TimestampYearMonth(b.ModifiedTime.Unix())
s, mock := setupDB(t)
mock.ExpectBegin()
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? ORDER BY `bucket_traffic`.`bucket_id` LIMIT 1 FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
"free_quota_size", "charged_quota_size", "modified_time"}).AddRow(b.BucketID, yearMonth, b.BucketName, b.ReadConsumedSize,
b.FreeQuotaConsumedSize, b.FreeQuotaSize, b.ChargedQuotaSize, b.ModifiedTime))

mock.ExpectExec("UPDATE `bucket_traffic` SET `read_consumed_size`=?,`free_quota_consumed_size`=?,`modified_time`=? WHERE `bucket_id` = ?").
mock.ExpectExec("UPDATE `bucket_traffic` SET `read_consumed_size`=?,`free_quota_consumed_size`=?,`free_quota_size`=?,`modified_time`=? WHERE `bucket_id` = ? ").
WithArgs(sqlmock.AnyArg(), 25, 24, sqlmock.AnyArg(), record.BucketID).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit()

Expand Down Expand Up @@ -136,7 +136,7 @@ func TestSpDBImpl_CheckQuotaAndAddReadRecordFailure1(t *testing.T) {
FreeQuotaSize: 10,
}
s, mock := setupDB(t)
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? ORDER BY `bucket_traffic`.`bucket_id` LIMIT 1").
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? ORDER BY `bucket_traffic`.`bucket_id` LIMIT 1 FOR UPDATE").
WillReturnError(mockDBInternalError)
err := s.CheckQuotaAndAddReadRecord(record, quota)
assert.Contains(t, err.Error(), mockDBInternalError.Error())
Expand All @@ -159,7 +159,7 @@ func TestSpDBImpl_CheckQuotaAndAddReadRecordFailure2(t *testing.T) {
}
s, mock := setupDB(t)
mock.ExpectBegin()
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? FOR UPDATE").WillReturnError(gorm.ErrRecordNotFound)
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? ORDER BY `bucket_traffic`.`bucket_id` LIMIT 1 FOR UPDATE").WillReturnError(gorm.ErrRecordNotFound)

err := s.CheckQuotaAndAddReadRecord(record, quota)
assert.Contains(t, err.Error(), noRecordInTrafficErr)
Expand Down Expand Up @@ -193,7 +193,7 @@ func TestSpDBImpl_CheckQuotaAndAddReadRecordFailure3(t *testing.T) {
yearMonth := TimestampYearMonth(b.ModifiedTime.Unix())
s, mock := setupDB(t)
mock.ExpectBegin()
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? ORDER BY `bucket_traffic`.`bucket_id` LIMIT 1 FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
"free_quota_size", "charged_quota_size", "modified_time"}).AddRow(b.BucketID, yearMonth, b.BucketName, b.ReadConsumedSize,
b.FreeQuotaConsumedSize, b.FreeQuotaSize, b.ChargedQuotaSize, b.ModifiedTime))

Expand Down Expand Up @@ -232,16 +232,16 @@ func TestSpDBImpl_CheckQuotaAndAddReadRecordFailure4(t *testing.T) {
yearMonth := TimestampYearMonth(b.ModifiedTime.Unix())
s, mock := setupDB(t)
mock.ExpectBegin()
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? ORDER BY `bucket_traffic`.`bucket_id` LIMIT 1 FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
"free_quota_size", "charged_quota_size", "modified_time"}).AddRow(b.BucketID, yearMonth, b.BucketName, b.ReadConsumedSize,
b.FreeQuotaConsumedSize, b.FreeQuotaSize, b.ChargedQuotaSize, b.ModifiedTime))

mock.ExpectExec("UPDATE `bucket_traffic` SET `charged_quota_size`=?,`modified_time`=? WHERE `bucket_id` = ?").
WithArgs(20, sqlmock.AnyArg(), record.BucketID).
WillReturnResult(sqlmock.NewResult(1, 1))

mock.ExpectExec("UPDATE `bucket_traffic` SET `read_consumed_size`=?,`free_quota_consumed_size`=?,`modified_time`=? WHERE `bucket_id` = ?").
WithArgs(sqlmock.AnyArg(), 11, sqlmock.AnyArg(), record.BucketID).
mock.ExpectExec("UPDATE `bucket_traffic` SET `read_consumed_size`=?,`free_quota_consumed_size`=?,`free_quota_size`=?,`modified_time`=? WHERE `bucket_id` = ? ").
WithArgs(sqlmock.AnyArg(), 101, 24, sqlmock.AnyArg(), record.BucketID).
WillReturnError(mockDBInternalError)

mock.ExpectRollback()
Expand Down Expand Up @@ -277,17 +277,17 @@ func TestSpDBImpl_CheckQuotaAndAddReadRecordFailure5(t *testing.T) {
yearMonth := TimestampYearMonth(b.ModifiedTime.Unix())
s, mock := setupDB(t)
mock.ExpectBegin()
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? ORDER BY `bucket_traffic`.`bucket_id` LIMIT 1 FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
"free_quota_size", "charged_quota_size", "modified_time"}).AddRow(b.BucketID, yearMonth, b.BucketName, b.ReadConsumedSize,
b.FreeQuotaConsumedSize, b.FreeQuotaSize, b.ChargedQuotaSize, b.ModifiedTime))

mock.ExpectExec("UPDATE `bucket_traffic` SET `charged_quota_size`=?,`modified_time`=? WHERE `bucket_id` = ?").
WithArgs(20, sqlmock.AnyArg(), record.BucketID).
WillReturnResult(sqlmock.NewResult(1, 1))

mock.ExpectExec("UPDATE `bucket_traffic` SET `read_consumed_size`=?,`free_quota_consumed_size`=?,`modified_time`=? WHERE `bucket_id` = ?").
WithArgs(sqlmock.AnyArg(), 11, sqlmock.AnyArg(), record.BucketID).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec("UPDATE `bucket_traffic` SET `read_consumed_size`=?,`free_quota_consumed_size`=?,`free_quota_size`=?,`modified_time`=? WHERE `bucket_id` = ? ").
WithArgs(sqlmock.AnyArg(), 101, 24, sqlmock.AnyArg(), record.BucketID).
WillReturnError(mockDBInternalError)

mock.ExpectCommit()
mock.ExpectBegin()
Expand All @@ -307,7 +307,7 @@ func TestSpDBImpl_CheckQuotaAndAddReadRecordFailure6(t *testing.T) {
UserAddress: "mockUserAddress",
BucketName: "mockBucketName",
ObjectName: "mockObjectName",
ReadSize: 1,
ReadSize: 25,
ReadTimestampUs: 1,
}
quota := &corespdb.BucketQuota{
Expand All @@ -319,21 +319,25 @@ func TestSpDBImpl_CheckQuotaAndAddReadRecordFailure6(t *testing.T) {
BucketName: "mockBucketName",
ReadConsumedSize: 60,
FreeQuotaConsumedSize: 25,
FreeQuotaSize: 25,
FreeQuotaSize: 3,
ChargedQuotaSize: 30,
ModifiedTime: time.Now(),
}
yearMonth := TimestampYearMonth(b.ModifiedTime.Unix())
s, mock := setupDB(t)
mock.ExpectBegin()
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? ORDER BY `bucket_traffic`.`bucket_id` LIMIT 1 FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
"free_quota_size", "charged_quota_size", "modified_time"}).AddRow(b.BucketID, yearMonth, b.BucketName, b.ReadConsumedSize,
b.FreeQuotaConsumedSize, b.FreeQuotaSize, b.ChargedQuotaSize, b.ModifiedTime))

mock.ExpectExec("UPDATE `bucket_traffic` SET `charged_quota_size`=?,`modified_time`=? WHERE `bucket_id` = ?").
WithArgs(20, sqlmock.AnyArg(), record.BucketID).
WillReturnResult(sqlmock.NewResult(1, 1))

mock.ExpectExec("UPDATE `bucket_traffic` SET `read_consumed_size`=?,`free_quota_consumed_size`=?,`free_quota_size`=?,`modified_time`=? WHERE `bucket_id` = ? ").
WithArgs(sqlmock.AnyArg(), 101, 24, sqlmock.AnyArg(), record.BucketID).
WillReturnResult(sqlmock.NewResult(1, 1))

mock.ExpectCommit()
err := s.CheckQuotaAndAddReadRecord(record, quota)
assert.Equal(t, ErrCheckQuotaEnough, err)
Expand Down Expand Up @@ -366,7 +370,7 @@ func TestSpDBImpl_CheckQuotaAndAddReadRecordFailure7(t *testing.T) {
yearMonth := TimestampYearMonth(b.ModifiedTime.Unix())
s, mock := setupDB(t)
mock.ExpectBegin()
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
mock.ExpectQuery("SELECT * FROM `bucket_traffic` WHERE bucket_id = ? and month = ? ORDER BY `bucket_traffic`.`bucket_id` LIMIT 1 FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"bucket_id", "year_month", "bucket_name", "read_consumed_size", "free_quota_consumed_size",
"free_quota_size", "charged_quota_size", "modified_time"}).AddRow(b.BucketID, yearMonth, b.BucketName, b.ReadConsumedSize,
b.FreeQuotaConsumedSize, b.FreeQuotaSize, b.ChargedQuotaSize, b.ModifiedTime))

Expand Down

0 comments on commit 9b4007a

Please sign in to comment.