-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
80478b1
commit c83475c
Showing
17 changed files
with
274 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package redisrepo | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/orbs-network/order-book/models" | ||
"github.com/orbs-network/order-book/utils/logger" | ||
"github.com/orbs-network/order-book/utils/logger/logctx" | ||
"github.com/redis/go-redis/v9" | ||
) | ||
|
||
// This should be used to store FILLED orders in Redis. | ||
// | ||
// `StoreOpenOrder` or `StoreOpenOrders` should be used to store unfilled or partially filled orders. | ||
func (r *redisRepository) StoreFilledOrders(ctx context.Context, orders []models.Order) error { | ||
transaction := r.client.TxPipeline() | ||
|
||
for _, order := range orders { | ||
err := storeFilledOrderTx(ctx, transaction, order) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
} | ||
_, err := transaction.Exec(ctx) | ||
if err != nil { | ||
logctx.Error(ctx, "failed to store filled orders in Redis", logger.Error(err), logger.Strings("orderIds", models.OrderIdsToStrings(ctx, &orders))) | ||
return fmt.Errorf("failed to store filled orders in Redis: %v", err) | ||
} | ||
return nil | ||
} | ||
|
||
func storeFilledOrderTx(ctx context.Context, transaction redis.Pipeliner, order models.Order) error { | ||
// 1. Remove the order from the user's open orders set | ||
userOrdersKey := CreateUserOpenOrdersKey(order.UserId) | ||
transaction.ZRem(ctx, userOrdersKey, order.Id.String()) | ||
|
||
// 2. Store the order in the filled orders set | ||
userFilledOrdersKey := CreateUserFilledOrdersKey(order.UserId) | ||
userFilledOrdersScore := float64(order.Timestamp.UTC().UnixNano()) | ||
transaction.ZAdd(ctx, userFilledOrdersKey, redis.Z{ | ||
Score: userFilledOrdersScore, | ||
Member: order.Id.String(), | ||
}) | ||
|
||
// 3. Remove the order from the buy/sell prices set for that pair | ||
buyPricesKey := CreateBuySidePricesKey(order.Symbol) | ||
sellPricesKey := CreateSellSidePricesKey(order.Symbol) | ||
if order.Side == models.BUY { | ||
transaction.ZRem(ctx, buyPricesKey, order.Id.String()) | ||
} else { | ||
transaction.ZRem(ctx, sellPricesKey, order.Id.String()) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package redisrepo | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/go-redis/redismock/v9" | ||
"github.com/orbs-network/order-book/models" | ||
"github.com/redis/go-redis/v9" | ||
"github.com/shopspring/decimal" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestRedisRepository_StoreFilledOrders(t *testing.T) { | ||
timestamp := time.Date(2023, 10, 10, 12, 0, 0, 0, time.UTC) | ||
|
||
t.Run("store filled order success (buy side) - should remove order from user open orders set, add order to user filled orders set, remove order from buy prices sorted set", func(t *testing.T) { | ||
var buyOrder = models.Order{ | ||
Id: orderId, | ||
ClientOId: clientOId, | ||
Price: price, | ||
Size: size, | ||
Symbol: symbol, | ||
Side: models.BUY, | ||
Timestamp: timestamp, | ||
SizePending: decimal.Zero, | ||
SizeFilled: size, | ||
UserId: userId, | ||
} | ||
|
||
db, mock := redismock.NewClientMock() | ||
|
||
repo := &redisRepository{ | ||
client: db, | ||
} | ||
|
||
mock.ExpectTxPipeline() | ||
mock.ExpectZRem(CreateUserOpenOrdersKey(buyOrder.UserId), buyOrder.Id.String()).SetVal(1) | ||
mock.ExpectZAdd(CreateUserFilledOrdersKey(buyOrder.UserId), redis.Z{ | ||
Score: float64(timestamp.UnixNano()), | ||
Member: buyOrder.Id.String(), | ||
}).SetVal(1) | ||
mock.ExpectZRem(CreateBuySidePricesKey(buyOrder.Symbol), buyOrder.Id.String()).SetVal(1) | ||
mock.ExpectTxPipelineExec() | ||
|
||
err := repo.StoreFilledOrders(ctx, []models.Order{buyOrder}) | ||
|
||
assert.NoError(t, err, "should not return error") | ||
}) | ||
|
||
t.Run("store filled order success (sell side) - should remove order from user open orders set, add order to user filled orders set, remove order from sell prices sorted set", func(t *testing.T) { | ||
var sellOrder = models.Order{ | ||
Id: orderId, | ||
ClientOId: clientOId, | ||
Price: price, | ||
Size: size, | ||
Symbol: symbol, | ||
Side: models.SELL, | ||
Timestamp: timestamp, | ||
SizePending: decimal.Zero, | ||
SizeFilled: size, | ||
UserId: userId, | ||
} | ||
|
||
db, mock := redismock.NewClientMock() | ||
|
||
repo := &redisRepository{ | ||
client: db, | ||
} | ||
|
||
mock.ExpectTxPipeline() | ||
mock.ExpectZRem(CreateUserOpenOrdersKey(sellOrder.UserId), sellOrder.Id.String()).SetVal(1) | ||
mock.ExpectZAdd(CreateUserFilledOrdersKey(sellOrder.UserId), redis.Z{ | ||
Score: float64(timestamp.UnixNano()), | ||
Member: sellOrder.Id.String(), | ||
}).SetVal(1) | ||
mock.ExpectZRem(CreateSellSidePricesKey(sellOrder.Symbol), sellOrder.Id.String()).SetVal(1) | ||
mock.ExpectTxPipelineExec() | ||
|
||
err := repo.StoreFilledOrders(ctx, []models.Order{sellOrder}) | ||
|
||
assert.NoError(t, err, "should not return error") | ||
}) | ||
|
||
t.Run("store filled order unexpected error - should return error", func(t *testing.T) { | ||
var sellOrder = models.Order{ | ||
UserId: userId, | ||
} | ||
|
||
db, mock := redismock.NewClientMock() | ||
|
||
repo := &redisRepository{ | ||
client: db, | ||
} | ||
|
||
mock.ExpectTxPipeline() | ||
mock.ExpectZRem(CreateUserOpenOrdersKey(sellOrder.UserId), sellOrder.Id.String()).SetErr(assert.AnError) | ||
|
||
err := repo.StoreFilledOrders(ctx, []models.Order{sellOrder}) | ||
|
||
assert.ErrorContains(t, err, "failed to store filled orders in Redis") | ||
assert.NoError(t, mock.ExpectationsWereMet()) | ||
}) | ||
} |
Oops, something went wrong.