2019-03-27 10:24:35 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package satellitedb
|
|
|
|
|
|
|
|
import (
|
2019-09-06 15:49:30 +01:00
|
|
|
"bytes"
|
2019-03-27 10:24:35 +00:00
|
|
|
"context"
|
2019-04-01 21:14:58 +01:00
|
|
|
"database/sql"
|
2019-06-10 15:58:28 +01:00
|
|
|
"sort"
|
2019-03-28 20:09:23 +00:00
|
|
|
"time"
|
2019-03-27 10:24:35 +00:00
|
|
|
|
2019-06-25 16:58:42 +01:00
|
|
|
"github.com/skyrings/skyring-common/tools/uuid"
|
2019-08-15 20:05:43 +01:00
|
|
|
"github.com/zeebo/errs"
|
2019-06-10 15:58:28 +01:00
|
|
|
|
2019-03-27 10:24:35 +00:00
|
|
|
"storj.io/storj/pkg/pb"
|
2019-03-28 20:09:23 +00:00
|
|
|
"storj.io/storj/pkg/storj"
|
2019-11-14 19:46:15 +00:00
|
|
|
"storj.io/storj/private/dbutil/pgutil"
|
2019-04-04 15:42:01 +01:00
|
|
|
"storj.io/storj/satellite/orders"
|
2019-03-27 10:24:35 +00:00
|
|
|
dbx "storj.io/storj/satellite/satellitedb/dbx"
|
|
|
|
)
|
|
|
|
|
2019-04-01 21:14:58 +01:00
|
|
|
const defaultIntervalSeconds = int(time.Hour / time.Second)
|
|
|
|
|
2019-09-06 15:49:30 +01:00
|
|
|
var (
|
|
|
|
// ErrDifferentStorageNodes is returned when ProcessOrders gets orders from different storage nodes.
|
|
|
|
ErrDifferentStorageNodes = errs.Class("different storage nodes")
|
|
|
|
)
|
|
|
|
|
2019-03-27 10:24:35 +00:00
|
|
|
type ordersDB struct {
|
2019-12-14 02:29:54 +00:00
|
|
|
db *satelliteDB
|
2019-03-27 10:24:35 +00:00
|
|
|
}
|
|
|
|
|
2019-04-01 21:14:58 +01:00
|
|
|
// CreateSerialInfo creates serial number entry in database
|
2019-06-04 12:55:38 +01:00
|
|
|
func (db *ordersDB) CreateSerialInfo(ctx context.Context, serialNumber storj.SerialNumber, bucketID []byte, limitExpiration time.Time) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-09-12 18:31:50 +01:00
|
|
|
return db.db.CreateNoReturn_SerialNumber(
|
2019-03-28 20:09:23 +00:00
|
|
|
ctx,
|
|
|
|
dbx.SerialNumber_SerialNumber(serialNumber.Bytes()),
|
|
|
|
dbx.SerialNumber_BucketId(bucketID),
|
|
|
|
dbx.SerialNumber_ExpiresAt(limitExpiration),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-08-27 18:12:38 +01:00
|
|
|
// DeleteExpiredSerials deletes all expired serials in serial_number and used_serials table.
|
|
|
|
func (db *ordersDB) DeleteExpiredSerials(ctx context.Context, now time.Time) (_ int, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
count, err := db.db.Delete_SerialNumber_By_ExpiresAt_LessOrEqual(ctx, dbx.SerialNumber_ExpiresAt(now))
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return int(count), nil
|
|
|
|
}
|
|
|
|
|
2019-04-01 21:14:58 +01:00
|
|
|
// UseSerialNumber creates serial number entry in database
|
2019-06-04 12:55:38 +01:00
|
|
|
func (db *ordersDB) UseSerialNumber(ctx context.Context, serialNumber storj.SerialNumber, storageNodeID storj.NodeID) (_ []byte, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-04-01 21:14:58 +01:00
|
|
|
statement := db.db.Rebind(
|
|
|
|
`INSERT INTO used_serials (serial_number_id, storage_node_id)
|
|
|
|
SELECT id, ? FROM serial_numbers WHERE serial_number = ?`,
|
|
|
|
)
|
2019-06-04 12:55:38 +01:00
|
|
|
_, err = db.db.ExecContext(ctx, statement, storageNodeID.Bytes(), serialNumber.Bytes())
|
2019-04-01 21:14:58 +01:00
|
|
|
if err != nil {
|
2019-10-18 22:27:57 +01:00
|
|
|
if pgutil.IsConstraintError(err) {
|
2019-04-04 15:42:01 +01:00
|
|
|
return nil, orders.ErrUsingSerialNumber.New("serial number already used")
|
|
|
|
}
|
2019-04-01 21:14:58 +01:00
|
|
|
return nil, err
|
2019-03-27 10:24:35 +00:00
|
|
|
}
|
|
|
|
|
2019-04-01 21:14:58 +01:00
|
|
|
dbxSerialNumber, err := db.db.Find_SerialNumber_By_SerialNumber(
|
|
|
|
ctx,
|
|
|
|
dbx.SerialNumber_SerialNumber(serialNumber.Bytes()),
|
|
|
|
)
|
2019-03-27 10:24:35 +00:00
|
|
|
if err != nil {
|
2019-04-01 21:14:58 +01:00
|
|
|
return nil, err
|
2019-03-27 10:24:35 +00:00
|
|
|
}
|
2019-04-04 15:42:01 +01:00
|
|
|
if dbxSerialNumber == nil {
|
|
|
|
return nil, orders.ErrUsingSerialNumber.New("serial number not found")
|
|
|
|
}
|
2019-04-01 21:14:58 +01:00
|
|
|
return dbxSerialNumber.BucketId, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateBucketBandwidthAllocation updates 'allocated' bandwidth for given bucket
|
2019-06-25 16:58:42 +01:00
|
|
|
func (db *ordersDB) UpdateBucketBandwidthAllocation(ctx context.Context, projectID uuid.UUID, bucketName []byte, action pb.PieceAction, amount int64, intervalStart time.Time) (err error) {
|
2019-06-04 12:55:38 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-04-01 21:14:58 +01:00
|
|
|
statement := db.db.Rebind(
|
2019-04-02 19:21:18 +01:00
|
|
|
`INSERT INTO bucket_bandwidth_rollups (bucket_name, project_id, interval_start, interval_seconds, action, inline, allocated, settled)
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
|
|
ON CONFLICT(bucket_name, project_id, interval_start, action)
|
2019-04-01 21:14:58 +01:00
|
|
|
DO UPDATE SET allocated = bucket_bandwidth_rollups.allocated + ?`,
|
|
|
|
)
|
2019-06-04 12:55:38 +01:00
|
|
|
_, err = db.db.ExecContext(ctx, statement,
|
2019-06-21 16:38:37 +01:00
|
|
|
bucketName, projectID[:], intervalStart, defaultIntervalSeconds, action, 0, uint64(amount), 0, uint64(amount),
|
2019-04-02 19:21:18 +01:00
|
|
|
)
|
2019-03-27 10:24:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-04-01 21:14:58 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateBucketBandwidthSettle updates 'settled' bandwidth for given bucket
|
2019-06-25 16:58:42 +01:00
|
|
|
func (db *ordersDB) UpdateBucketBandwidthSettle(ctx context.Context, projectID uuid.UUID, bucketName []byte, action pb.PieceAction, amount int64, intervalStart time.Time) (err error) {
|
2019-06-04 12:55:38 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-04-01 21:14:58 +01:00
|
|
|
statement := db.db.Rebind(
|
2019-04-02 19:21:18 +01:00
|
|
|
`INSERT INTO bucket_bandwidth_rollups (bucket_name, project_id, interval_start, interval_seconds, action, inline, allocated, settled)
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
|
|
ON CONFLICT(bucket_name, project_id, interval_start, action)
|
2019-04-01 21:14:58 +01:00
|
|
|
DO UPDATE SET settled = bucket_bandwidth_rollups.settled + ?`,
|
2019-03-27 10:24:35 +00:00
|
|
|
)
|
2019-06-04 12:55:38 +01:00
|
|
|
_, err = db.db.ExecContext(ctx, statement,
|
2019-06-21 16:38:37 +01:00
|
|
|
bucketName, projectID[:], intervalStart, defaultIntervalSeconds, action, 0, 0, uint64(amount), uint64(amount),
|
2019-04-02 19:21:18 +01:00
|
|
|
)
|
2019-03-27 10:24:35 +00:00
|
|
|
if err != nil {
|
2019-04-01 21:14:58 +01:00
|
|
|
return err
|
2019-03-27 10:24:35 +00:00
|
|
|
}
|
2019-04-01 21:14:58 +01:00
|
|
|
return nil
|
2019-03-27 10:24:35 +00:00
|
|
|
}
|
|
|
|
|
2019-04-01 21:14:58 +01:00
|
|
|
// UpdateBucketBandwidthInline updates 'inline' bandwidth for given bucket
|
2019-06-25 16:58:42 +01:00
|
|
|
func (db *ordersDB) UpdateBucketBandwidthInline(ctx context.Context, projectID uuid.UUID, bucketName []byte, action pb.PieceAction, amount int64, intervalStart time.Time) (err error) {
|
2019-06-04 12:55:38 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-04-01 21:14:58 +01:00
|
|
|
statement := db.db.Rebind(
|
2019-04-02 19:21:18 +01:00
|
|
|
`INSERT INTO bucket_bandwidth_rollups (bucket_name, project_id, interval_start, interval_seconds, action, inline, allocated, settled)
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
|
|
ON CONFLICT(bucket_name, project_id, interval_start, action)
|
2019-04-01 21:14:58 +01:00
|
|
|
DO UPDATE SET inline = bucket_bandwidth_rollups.inline + ?`,
|
|
|
|
)
|
2019-06-04 12:55:38 +01:00
|
|
|
_, err = db.db.ExecContext(ctx, statement,
|
2019-06-21 16:38:37 +01:00
|
|
|
bucketName, projectID[:], intervalStart, defaultIntervalSeconds, action, uint64(amount), 0, 0, uint64(amount),
|
2019-04-02 19:21:18 +01:00
|
|
|
)
|
2019-03-27 10:24:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-04-01 21:14:58 +01:00
|
|
|
return nil
|
|
|
|
}
|
2019-03-27 10:24:35 +00:00
|
|
|
|
2019-04-01 21:14:58 +01:00
|
|
|
// UpdateStoragenodeBandwidthAllocation updates 'allocated' bandwidth for given storage node
|
2019-06-10 15:58:28 +01:00
|
|
|
func (db *ordersDB) UpdateStoragenodeBandwidthAllocation(ctx context.Context, storageNodes []storj.NodeID, action pb.PieceAction, amount int64, intervalStart time.Time) (err error) {
|
2019-06-04 12:55:38 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-06-10 15:58:28 +01:00
|
|
|
|
2019-10-18 22:27:57 +01:00
|
|
|
// sort nodes to avoid update deadlock
|
|
|
|
sort.Sort(storj.NodeIDList(storageNodes))
|
2019-06-10 15:58:28 +01:00
|
|
|
|
2019-10-18 22:27:57 +01:00
|
|
|
_, err = db.db.ExecContext(ctx, db.db.Rebind(`
|
|
|
|
INSERT INTO storagenode_bandwidth_rollups
|
|
|
|
(storagenode_id, interval_start, interval_seconds, action, allocated, settled)
|
|
|
|
SELECT unnest($1::bytea[]), $2, $3, $4, $5, $6
|
|
|
|
ON CONFLICT(storagenode_id, interval_start, action)
|
|
|
|
DO UPDATE SET allocated = storagenode_bandwidth_rollups.allocated + excluded.allocated
|
|
|
|
`), postgresNodeIDList(storageNodes), intervalStart, defaultIntervalSeconds, action, uint64(amount), 0)
|
2019-06-10 15:58:28 +01:00
|
|
|
|
2019-10-18 22:27:57 +01:00
|
|
|
return Error.Wrap(err)
|
2019-04-01 21:14:58 +01:00
|
|
|
}
|
2019-03-27 10:24:35 +00:00
|
|
|
|
2019-04-04 16:20:59 +01:00
|
|
|
// UpdateStoragenodeBandwidthSettle updates 'settled' bandwidth for given storage node for the given intervalStart time
|
2019-06-04 12:55:38 +01:00
|
|
|
func (db *ordersDB) UpdateStoragenodeBandwidthSettle(ctx context.Context, storageNode storj.NodeID, action pb.PieceAction, amount int64, intervalStart time.Time) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-04-01 21:14:58 +01:00
|
|
|
statement := db.db.Rebind(
|
2019-04-02 19:21:18 +01:00
|
|
|
`INSERT INTO storagenode_bandwidth_rollups (storagenode_id, interval_start, interval_seconds, action, allocated, settled)
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
2019-04-01 21:14:58 +01:00
|
|
|
ON CONFLICT(storagenode_id, interval_start, action)
|
|
|
|
DO UPDATE SET settled = storagenode_bandwidth_rollups.settled + ?`,
|
|
|
|
)
|
2019-06-04 12:55:38 +01:00
|
|
|
_, err = db.db.ExecContext(ctx, statement,
|
2019-04-02 19:21:18 +01:00
|
|
|
storageNode.Bytes(), intervalStart, defaultIntervalSeconds, action, 0, uint64(amount), uint64(amount),
|
|
|
|
)
|
2019-03-27 10:24:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-04-01 21:14:58 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBucketBandwidth gets total bucket bandwidth from period of time
|
2019-06-25 16:58:42 +01:00
|
|
|
func (db *ordersDB) GetBucketBandwidth(ctx context.Context, projectID uuid.UUID, bucketName []byte, from, to time.Time) (_ int64, err error) {
|
2019-06-04 12:55:38 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-04-01 21:14:58 +01:00
|
|
|
var sum *int64
|
2019-04-02 19:21:18 +01:00
|
|
|
query := `SELECT SUM(settled) FROM bucket_bandwidth_rollups WHERE bucket_name = ? AND project_id = ? AND interval_start > ? AND interval_start <= ?`
|
2019-06-21 16:38:37 +01:00
|
|
|
err = db.db.QueryRow(db.db.Rebind(query), bucketName, projectID[:], from, to).Scan(&sum)
|
2019-04-01 21:14:58 +01:00
|
|
|
if err == sql.ErrNoRows || sum == nil {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
return *sum, err
|
|
|
|
}
|
2019-03-27 10:24:35 +00:00
|
|
|
|
2019-04-01 21:14:58 +01:00
|
|
|
// GetStorageNodeBandwidth gets total storage node bandwidth from period of time
|
2019-06-04 12:55:38 +01:00
|
|
|
func (db *ordersDB) GetStorageNodeBandwidth(ctx context.Context, nodeID storj.NodeID, from, to time.Time) (_ int64, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-04-01 21:14:58 +01:00
|
|
|
var sum *int64
|
|
|
|
query := `SELECT SUM(settled) FROM storagenode_bandwidth_rollups WHERE storagenode_id = ? AND interval_start > ? AND interval_start <= ?`
|
2019-06-04 12:55:38 +01:00
|
|
|
err = db.db.QueryRow(db.db.Rebind(query), nodeID.Bytes(), from, to).Scan(&sum)
|
2019-04-01 21:14:58 +01:00
|
|
|
if err == sql.ErrNoRows || sum == nil {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
return *sum, err
|
|
|
|
}
|
2019-03-27 10:24:35 +00:00
|
|
|
|
2019-04-01 21:14:58 +01:00
|
|
|
// UnuseSerialNumber removes pair serial number -> storage node id from database
|
2019-06-04 12:55:38 +01:00
|
|
|
func (db *ordersDB) UnuseSerialNumber(ctx context.Context, serialNumber storj.SerialNumber, storageNodeID storj.NodeID) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-04-01 21:14:58 +01:00
|
|
|
statement := `DELETE FROM used_serials WHERE storage_node_id = ? AND
|
|
|
|
serial_number_id IN (SELECT id FROM serial_numbers WHERE serial_number = ?)`
|
2019-06-04 12:55:38 +01:00
|
|
|
_, err = db.db.ExecContext(ctx, db.db.Rebind(statement), storageNodeID.Bytes(), serialNumber.Bytes())
|
2019-04-01 21:14:58 +01:00
|
|
|
return err
|
2019-03-27 10:24:35 +00:00
|
|
|
}
|
2019-08-15 20:05:43 +01:00
|
|
|
|
2019-09-06 15:49:30 +01:00
|
|
|
// ProcessOrders take a list of order requests and "settles" them in one transaction.
|
|
|
|
//
|
|
|
|
// ProcessOrders requires that all orders come from the same storage node.
|
2019-08-19 14:36:11 +01:00
|
|
|
func (db *ordersDB) ProcessOrders(ctx context.Context, requests []*orders.ProcessOrderRequest) (responses []*orders.ProcessOrderResponse, err error) {
|
2019-08-15 20:05:43 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
if len(requests) == 0 {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-09-06 15:49:30 +01:00
|
|
|
// check that all requests are from the same storage node
|
|
|
|
storageNodeID := requests[0].OrderLimit.StorageNodeId
|
|
|
|
for _, req := range requests[1:] {
|
|
|
|
if req.OrderLimit.StorageNodeId != storageNodeID {
|
|
|
|
return nil, ErrDifferentStorageNodes.New("requests from different storage nodes %v and %v", storageNodeID, req.OrderLimit.StorageNodeId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort requests by serial number, all of them should be from the same storage node
|
|
|
|
sort.Slice(requests, func(i, k int) bool {
|
|
|
|
return requests[i].OrderLimit.SerialNumber.Less(requests[k].OrderLimit.SerialNumber)
|
|
|
|
})
|
|
|
|
|
2019-08-15 20:05:43 +01:00
|
|
|
tx, err := db.db.Begin()
|
|
|
|
if err != nil {
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
if err == nil {
|
|
|
|
err = tx.Commit()
|
|
|
|
} else {
|
|
|
|
err = errs.Combine(err, tx.Rollback())
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
now := time.Now().UTC()
|
|
|
|
intervalStart := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
|
|
|
|
|
2019-09-06 15:49:30 +01:00
|
|
|
rejected := make(map[storj.SerialNumber]bool)
|
|
|
|
bucketBySerial := make(map[storj.SerialNumber][]byte)
|
2019-08-15 20:05:43 +01:00
|
|
|
|
2019-09-06 15:49:30 +01:00
|
|
|
// load the bucket id and insert into used serials table
|
2019-08-15 20:05:43 +01:00
|
|
|
for _, request := range requests {
|
2019-09-06 15:49:30 +01:00
|
|
|
row := tx.QueryRow(db.db.Rebind(`
|
|
|
|
SELECT id, bucket_id
|
|
|
|
FROM serial_numbers
|
|
|
|
WHERE serial_number = ?
|
|
|
|
`), request.OrderLimit.SerialNumber)
|
|
|
|
|
|
|
|
var serialNumberID int64
|
|
|
|
var bucketID []byte
|
|
|
|
if err := row.Scan(&serialNumberID, &bucketID); err != nil {
|
|
|
|
rejected[request.OrderLimit.SerialNumber] = true
|
|
|
|
continue
|
2019-08-15 20:05:43 +01:00
|
|
|
}
|
2019-08-29 16:14:10 +01:00
|
|
|
|
2019-09-06 15:49:30 +01:00
|
|
|
var result sql.Result
|
|
|
|
var count int64
|
2019-08-29 16:14:10 +01:00
|
|
|
|
2019-09-06 15:49:30 +01:00
|
|
|
// try to insert the serial number
|
|
|
|
result, err = tx.Exec(db.db.Rebind(`
|
|
|
|
INSERT INTO used_serials(serial_number_id, storage_node_id)
|
|
|
|
VALUES (?, ?)
|
|
|
|
ON CONFLICT DO NOTHING
|
|
|
|
`), serialNumberID, storageNodeID)
|
2019-08-15 20:05:43 +01:00
|
|
|
if err != nil {
|
2019-09-06 15:49:30 +01:00
|
|
|
return nil, Error.Wrap(err)
|
2019-08-15 20:05:43 +01:00
|
|
|
}
|
2019-09-06 15:49:30 +01:00
|
|
|
|
|
|
|
// if we didn't update any rows, then it must already exist
|
|
|
|
count, err = result.RowsAffected()
|
2019-08-29 16:14:10 +01:00
|
|
|
if err != nil {
|
2019-09-04 14:02:20 +01:00
|
|
|
return nil, Error.Wrap(err)
|
2019-08-29 16:14:10 +01:00
|
|
|
}
|
2019-09-06 15:49:30 +01:00
|
|
|
if count == 0 {
|
|
|
|
rejected[request.OrderLimit.SerialNumber] = true
|
|
|
|
continue
|
|
|
|
}
|
2019-08-15 20:05:43 +01:00
|
|
|
|
2019-09-06 15:49:30 +01:00
|
|
|
bucketBySerial[request.OrderLimit.SerialNumber] = bucketID
|
2019-08-15 20:05:43 +01:00
|
|
|
}
|
|
|
|
|
2019-09-06 15:49:30 +01:00
|
|
|
// add up amount by action
|
|
|
|
var largestAction pb.PieceAction
|
|
|
|
amountByAction := map[pb.PieceAction]int64{}
|
|
|
|
for _, request := range requests {
|
|
|
|
if rejected[request.OrderLimit.SerialNumber] {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
limit, order := request.OrderLimit, request.Order
|
|
|
|
amountByAction[limit.Action] += order.Amount
|
|
|
|
if largestAction < limit.Action {
|
|
|
|
largestAction = limit.Action
|
|
|
|
}
|
2019-08-15 20:05:43 +01:00
|
|
|
}
|
2019-09-06 15:49:30 +01:00
|
|
|
|
|
|
|
// do action updates for storage node
|
|
|
|
for action := pb.PieceAction(0); action <= largestAction; action++ {
|
|
|
|
amount := amountByAction[action]
|
|
|
|
if amount == 0 {
|
|
|
|
continue
|
2019-08-15 20:05:43 +01:00
|
|
|
}
|
2019-09-06 15:49:30 +01:00
|
|
|
|
|
|
|
_, err := tx.Exec(db.db.Rebind(`
|
|
|
|
INSERT INTO storagenode_bandwidth_rollups
|
|
|
|
(storagenode_id, interval_start, interval_seconds, action, allocated, settled)
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
|
|
ON CONFLICT (storagenode_id, interval_start, action)
|
|
|
|
DO UPDATE SET settled = storagenode_bandwidth_rollups.settled + ?
|
|
|
|
`), storageNodeID, intervalStart, defaultIntervalSeconds, action, 0, amount, amount)
|
2019-08-15 20:05:43 +01:00
|
|
|
if err != nil {
|
2019-09-06 15:49:30 +01:00
|
|
|
return nil, Error.Wrap(err)
|
2019-08-15 20:05:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-06 15:49:30 +01:00
|
|
|
// sort bucket updates
|
|
|
|
type bucketUpdate struct {
|
|
|
|
bucketID []byte
|
|
|
|
action pb.PieceAction
|
|
|
|
amount int64
|
|
|
|
}
|
|
|
|
var bucketUpdates []bucketUpdate
|
2019-08-15 20:05:43 +01:00
|
|
|
for _, request := range requests {
|
2019-09-06 15:49:30 +01:00
|
|
|
if rejected[request.OrderLimit.SerialNumber] {
|
2019-08-15 20:05:43 +01:00
|
|
|
continue
|
|
|
|
}
|
2019-09-06 15:49:30 +01:00
|
|
|
limit, order := request.OrderLimit, request.Order
|
|
|
|
|
|
|
|
bucketUpdates = append(bucketUpdates, bucketUpdate{
|
|
|
|
bucketID: bucketBySerial[limit.SerialNumber],
|
|
|
|
action: limit.Action,
|
|
|
|
amount: order.Amount,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Slice(bucketUpdates, func(i, k int) bool {
|
|
|
|
compare := bytes.Compare(bucketUpdates[i].bucketID, bucketUpdates[k].bucketID)
|
|
|
|
if compare == 0 {
|
|
|
|
return bucketUpdates[i].action < bucketUpdates[k].action
|
2019-08-15 20:05:43 +01:00
|
|
|
}
|
2019-09-06 15:49:30 +01:00
|
|
|
return compare < 0
|
|
|
|
})
|
2019-08-15 20:05:43 +01:00
|
|
|
|
2019-09-06 15:49:30 +01:00
|
|
|
// do bucket updates
|
|
|
|
for _, update := range bucketUpdates {
|
|
|
|
projectID, bucketName, err := orders.SplitBucketID(update.bucketID)
|
2019-08-15 20:05:43 +01:00
|
|
|
if err != nil {
|
2019-09-06 15:49:30 +01:00
|
|
|
return nil, errs.Wrap(err)
|
2019-08-15 20:05:43 +01:00
|
|
|
}
|
|
|
|
|
2019-09-06 15:49:30 +01:00
|
|
|
_, err = tx.Exec(db.db.Rebind(`
|
|
|
|
INSERT INTO bucket_bandwidth_rollups
|
|
|
|
(bucket_name, project_id, interval_start, interval_seconds, action, inline, allocated, settled)
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
|
|
ON CONFLICT (bucket_name, project_id, interval_start, action)
|
|
|
|
DO UPDATE SET settled = bucket_bandwidth_rollups.settled + ?
|
|
|
|
`), bucketName, (*projectID)[:], intervalStart, defaultIntervalSeconds, update.action, 0, 0, update.amount, update.amount)
|
2019-08-15 20:05:43 +01:00
|
|
|
if err != nil {
|
2019-09-06 15:49:30 +01:00
|
|
|
return nil, Error.Wrap(err)
|
2019-08-15 20:05:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, request := range requests {
|
2019-09-06 15:49:30 +01:00
|
|
|
if !rejected[request.OrderLimit.SerialNumber] {
|
|
|
|
responses = append(responses, &orders.ProcessOrderResponse{
|
2019-08-15 20:05:43 +01:00
|
|
|
SerialNumber: request.OrderLimit.SerialNumber,
|
|
|
|
Status: pb.SettlementResponse_ACCEPTED,
|
2019-09-06 15:49:30 +01:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
responses = append(responses, &orders.ProcessOrderResponse{
|
|
|
|
SerialNumber: request.OrderLimit.SerialNumber,
|
|
|
|
Status: pb.SettlementResponse_REJECTED,
|
|
|
|
})
|
2019-08-15 20:05:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return responses, nil
|
|
|
|
}
|