Documentation ¶
Overview ¶
Package shopping provides rush shopping backend service.
Requirements ¶
The RESTful API is identified in spec.md, including the URL, logic semantics, request format and response status.
Some important constrains of rush shopping are highlighted as follows.
- Every item has a limited stock and can't be oversold. The stock constraint should be guaranteed when the order is made instead of managing the cart.
- Every user can create more than one cart, but the total of items in it must not more than three.
- Every user can't make more than one order.
The normal actions for a user to finish purchasing the order are as follows.
| /carts/xxx | /login --- /items --- /carts --- | /carts/xxx | --- /orders --- /pay | /carts/xxx |
Actually, a user could create carts before querying the items. Of course, a user could add items to cart or discard items by /carts/xxx in any times. Just make sure the total of items in the cart must not more than three.
The admin username and password are as follows.
| admin | username | password | | value | root | root |
And the username with the prefix "zero" means the corresponding the user's balance is zero, so she/he can't afford any item.
The CSV format for the users is the following.
| id | username | password | balance | | int | string | string | int |
The CSV format for the items is the following.
| id | price | stock | | int | int | int |
An implementation in our project ¶
We assume the followings:
The IDs of items are increasing from 1 continuously.
The ID of the (root) administrator user is 0.
The IDs of normal users are increasing from 1 continuously.
CartID is auto-increased from 1.
The data format in KV-Store could be referred in shop_kvformat.md.
Index ¶
- Constants
- Variables
- func AddItemTxnInit(args interface{}) (ret interface{}, errCode int)
- func PayOrderTxnInit(args interface{}) (ret interface{}, errCode int)
- func SubmitOrderTxnInit(args interface{}) (ret interface{}, errCode int)
- type AddItemArgs
- type AddItemTxnInitArgs
- type AddItemTxnInitRet
- type CartIDJson
- type CoordClients
- func (cs *CoordClients) AsyncAddItemTxn(cartIDStr, userToken string, itemID, addItemCnt int) (ok bool, txnID string)
- func (cs *CoordClients) AsyncPayOrderTxn(orderIDStr, userToken string, delta int) (ok bool, txnID string)
- func (cs *CoordClients) AsyncSubmitOrderTxn(cartIDStr, userToken string) (ok bool, txnID string)
- func (cs *CoordClients) LoadItemList(itemsCnt int) (ok bool)
- func (cs *CoordClients) SyncTxn(txnID string) (errCode int)
- type Item
- type ItemCount
- type LoginJson
- type Order
- type OrderDetail
- type OrderIDJson
- type PayOrderArgs
- type PayOrderTxnInitArgs
- type PayOrderTxnInitRet
- type ShardsClientHub
- type ShopServer
- type ShoppingTxnCoordinator
- func (stc *ShoppingTxnCoordinator) AsyncAddItemTxn(args *AddItemArgs, txnID *string) error
- func (stc *ShoppingTxnCoordinator) AsyncPayOrderTxn(args *PayOrderArgs, txnID *string) error
- func (stc *ShoppingTxnCoordinator) AsyncSubmitOrderTxn(args *SubmitOrderArgs, txnID *string) error
- func (stc *ShoppingTxnCoordinator) LoadItemList(itemsSize *int, reply *struct{}) error
- func (stc *ShoppingTxnCoordinator) Run()
- type ShoppingTxnKVStore
- func (skv *ShoppingTxnKVStore) CartAddItem(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
- func (skv *ShoppingTxnKVStore) CartExist(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
- func (skv *ShoppingTxnKVStore) ItemsStockMinus(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
- func (skv *ShoppingTxnKVStore) OrderRecord(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
- func (skv *ShoppingTxnKVStore) PayAdd(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
- func (skv *ShoppingTxnKVStore) PayMinus(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
- func (skv *ShoppingTxnKVStore) PayRecord(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
- type ShoppingTxnKVStoreService
- type SubmitOrderArgs
- type SubmitOrderTxnInitArgs
- type SubmitOrderTxnInitRet
- type TxnTask
- type UserIDAndPass
Constants ¶
const ( URLLogin = "/login" URLQueryItem = "/items" URLCreateCart = "/carts" URLAddItem = "/carts/" URLSubmitOrQueryOrder = "/orders" URLPayOrder = "/pay" URLQueryAllOrders = "/admin/orders" )
API URLs
const ( TokenKeyPrefix = "token:" OrderKeyPrefix = "order:" ItemsStockKeyPrefix = "items_stock:" ItemsPriceKeyPrefix = "items_price:" BalanceKeyPrefix = "balance:" CartIDMaxKey = "cartID" ItemsSizeKey = "items_size" )
Keys of kvstore.
const ( OrderPaidFlag = "P" // have been paid OrderUnpaidFlag = "W" // wait to be paid )
Flags for paid or unpaid status.
const ( TxnOK = 0 TxnNotFound = 1 << (iota - 1) // iota == 1 TxnNotAuth TxnCartEmpyt TxnOutOfStock // out of stock TxnItemOutOfLimit // most 3 items TxnOrderOutOfLimit // most one order for one person TxnOrderPaid TxnBalanceInsufficient )
Trans status.
const DefaultCoordClientPoolMaxSize = 100
DefaultCoordClientPoolMaxSize is the default size of connection pool for the Coordinator in 2pc.
const DefaultShardClientPoolMaxSize = 100
DefaultShardClientPoolMaxSize is the default size of connection pool for every shard of the whole KV-store.
const DefaultTaskMaxSize = 10000
DefaultTaskMaxSize is the maximum of TxnTasks waiting to be processed one by one.
const RootUserID = 0
RootUserID is specified.
Variables ¶
var ( UserAuthFailMsg = []byte("{\"code\":\"USER_AUTH_FAIL\",\"message\":\"用户名或密码错误\"}") MalformedJSONMsg = []byte("{\"code\": \"MALFORMED_JSON\",\"message\": \"格式错误\"}") EmptyRequestMsg = []byte("{\"code\": \"EMPTY_REQUEST\",\"message\": \"请求体为空\"}") InvalidAccessTokenMsg = []byte("{\"code\": \"INVALID_ACCESS_TOKEN\",\"message\": \"无效的令牌\"}") CartNotFoundMsg = []byte("{\"code\": \"CART_NOT_FOUND\", \"message\": \"篮子不存在\"}") CartEmptyMsg = []byte("{\"code\": \"CART_EMPTY\", \"message\": \"购物车为空\"}") NotAuthorizedCartMsg = []byte("{\"code\": \"NOT_AUTHORIZED_TO_ACCESS_CART\",\"message\": \"无权限访问指定的篮子\"}") ItemOutOfLimitMsg = []byte("{\"code\": \"ITEM_OUT_OF_LIMIT\",\"message\": \"篮子中物品数量超过了三个\"}") ItemNotFoundMsg = []byte("{\"code\": \"ITEM_NOT_FOUND\",\"message\": \"物品不存在\"}") ItemOutOfStockMsg = []byte("{\"code\": \"ITEM_OUT_OF_STOCK\", \"message\": \"物品库存不足\"}") OrderOutOfLimitMsg = []byte("{\"code\": \"ORDER_OUT_OF_LIMIT\",\"message\": \"每个用户只能下一单\"}") OrderNotFoundMsg = []byte("{\"code\": \"ORDER_NOT_FOUND\", \"message\": \"篮子不存在\"}") NotAuthorizedOrderMsg = []byte("{\"code\": \"NOT_AUTHORIZED_TO_ACCESS_ORDER\",\"message\": \"无权限访问指定的订单\"}") OrderPaidMsg = []byte("{\"code\": \"ORDER_PAID\",\"message\": \"订单已支付\"}") BalanceInsufficientMsg = []byte("{\"code\": \"BALANCE_INSUFFICIENT\",\"message\": \"余额不足\"}") )
JSON-format msgs for requests.
var RootUserToken = userID2Token(RootUserID)
RootUserToken is token for the root user.
Functions ¶
func AddItemTxnInit ¶
func AddItemTxnInit(args interface{}) (ret interface{}, errCode int)
AddItemTxnInit is the initial function for AddItem txn.
func PayOrderTxnInit ¶
func PayOrderTxnInit(args interface{}) (ret interface{}, errCode int)
PayOrderTxnInit is the initial function for PayOrder txn.
func SubmitOrderTxnInit ¶
func SubmitOrderTxnInit(args interface{}) (ret interface{}, errCode int)
SubmitOrderTxnInit is the initial function for SubmitOrder txn.
Types ¶
type AddItemArgs ¶
AddItemArgs is the argument of the AddItemTrans function.
type AddItemTxnInitArgs ¶
type AddItemTxnInitArgs struct { OrderKey string CartKey string CartIDStr string ItemID int AddItemCnt int }
AddItemTxnInitArgs is intial args for AddItem txn's initialization.
type AddItemTxnInitRet ¶
type AddItemTxnInitRet AddItemTxnInitArgs
AddItemTxnInitRet is return value for AddItem txn's initialization.
type CartIDJson ¶
type CartIDJson struct {
IDStr string `json:"cart_id"`
}
type CoordClients ¶
type CoordClients struct {
// contains filtered or unexported fields
}
CoordClients is the clients to connect to Coordinator.
func NewCoordClients ¶
func NewCoordClients(network, addr string, size int) *CoordClients
NewCoordClients inits a new coordinator.
func (*CoordClients) AsyncAddItemTxn ¶
func (cs *CoordClients) AsyncAddItemTxn(cartIDStr, userToken string, itemID, addItemCnt int) (ok bool, txnID string)
AsyncAddItemTxn submits AddItem Txn and returns immediately.
func (*CoordClients) AsyncPayOrderTxn ¶
func (cs *CoordClients) AsyncPayOrderTxn(orderIDStr, userToken string, delta int) (ok bool, txnID string)
AsyncPayOrderTxn submits PayOrder Txn and returns immediately.
func (*CoordClients) AsyncSubmitOrderTxn ¶
func (cs *CoordClients) AsyncSubmitOrderTxn(cartIDStr, userToken string) (ok bool, txnID string)
AsyncSubmitOrderTxn submits SubmitOrder Txn and returns immediately.
func (*CoordClients) LoadItemList ¶
func (cs *CoordClients) LoadItemList(itemsCnt int) (ok bool)
LoadItemList makes the coordinator load items records into the database.
func (*CoordClients) SyncTxn ¶
func (cs *CoordClients) SyncTxn(txnID string) (errCode int)
SyncTxn returns until the corresponding Txn get ended.
type OrderDetail ¶
OrderDetail is for GET /admin/orders
type OrderIDJson ¶
type OrderIDJson struct {
IDStr string `json:"order_id"`
}
type PayOrderArgs ¶
PayOrderArgs is the args for PayOrder txn.
type PayOrderTxnInitArgs ¶
type PayOrderTxnInitArgs struct { OrderKey string BalanceKey string RootBalanceKey string Delta int // contains filtered or unexported fields }
PayOrderTxnInitArgs is initial args for PayOrder txn's initialization.
type PayOrderTxnInitRet ¶
type PayOrderTxnInitRet PayOrderTxnInitArgs
PayOrderTxnInitRet is return value for PayOrder txn's initialization.
type ShardsClientHub ¶
type ShardsClientHub struct {
// contains filtered or unexported fields
}
ShardsClientHub is the client hub to connect to the databases directly.
func NewShardsClientHub ¶
func NewShardsClientHub(network string, srvAddrs []string, keyHashFunc twopc.KeyHashFunc, maxSizeForOne int) *ShardsClientHub
NewShardsClientHub inits a new ShardsClientHub.
func (*ShardsClientHub) Get ¶
func (h *ShardsClientHub) Get(key string) (ok bool, reply kv.Reply)
Get commands a get operation onto the specific shard.
type ShopServer ¶
type ShopServer struct { // All the followings are in the resident memory. // Item start from index 1. ItemListCache []Item ItemLock sync.Mutex // ItemsJSONCache is cache for querying items. ItemsJSONCache []byte // UserMap is the users' information. UserMap map[string]UserIDAndPass // MaxItemID is the same with the number of types of items. MaxItemID int // MaxUserID is the same with the number of normal users. MaxUserID int // contains filtered or unexported fields }
ShopServer is the web service for rush shopping.
func InitService ¶
func InitService(network, appAddr, coordAddr, userCsv, itemCsv string, kvstoreAddrs []string, keyHashFunc twopc.KeyHashFunc) *ShopServer
InitService inits the rush shopping web service and starts the service. Network is "tcp" or "unix" for the whole system. CoordAddr and kvstoreAddrs communication addresses for the Coordinator and shards. UserCsv and itemCsv is the CSV file path for users and items. KeyHashFunc is the hash function to distribute the key-value pair to the specific shard.
type ShoppingTxnCoordinator ¶
type ShoppingTxnCoordinator struct {
// contains filtered or unexported fields
}
ShoppingTxnCoordinator wraps the Coordinator with the RPC services to serve the txn logics.
func NewShoppingTxnCoordinator ¶
func NewShoppingTxnCoordinator(network, coord string, ppts []string, keyHashFunc twopc.KeyHashFunc, timeoutMs int64) *ShoppingTxnCoordinator
NewShoppingTxnCoordinator inits ShoppingTxnCoordinator service.
func (*ShoppingTxnCoordinator) AsyncAddItemTxn ¶
func (stc *ShoppingTxnCoordinator) AsyncAddItemTxn(args *AddItemArgs, txnID *string) error
AsyncAddItemTxn submits the AddItem txn to the tasks list, and then returns immediately.
func (*ShoppingTxnCoordinator) AsyncPayOrderTxn ¶
func (stc *ShoppingTxnCoordinator) AsyncPayOrderTxn(args *PayOrderArgs, txnID *string) error
AsyncPayOrderTxn submits PayOrder txn to the task list.
func (*ShoppingTxnCoordinator) AsyncSubmitOrderTxn ¶
func (stc *ShoppingTxnCoordinator) AsyncSubmitOrderTxn(args *SubmitOrderArgs, txnID *string) error
AsyncSubmitOrderTxn submits SubmitOrder txn to the task list.
func (*ShoppingTxnCoordinator) LoadItemList ¶
func (stc *ShoppingTxnCoordinator) LoadItemList(itemsSize *int, reply *struct{}) error
LoadItemList loads item info into the cahce for the slater rapid visiting.
func (*ShoppingTxnCoordinator) Run ¶
func (stc *ShoppingTxnCoordinator) Run()
Run executes the txns one by one from the task list.
type ShoppingTxnKVStore ¶
type ShoppingTxnKVStore struct { kv.KVStoreService // contains filtered or unexported fields }
ShoppingTxnKVStore supports the shopping transations. It wraps the KV-store with the shopping txn logics. The logics will be registered into the 2pc service, so the txns could be managed by the Coordinator and the Parcipants.
func NewShoppingTxnKVStore ¶
func NewShoppingTxnKVStore() *ShoppingTxnKVStore
NewShoppingTxnKVStore inits a new ShoppingTxnKVStore.
func (*ShoppingTxnKVStore) CartAddItem ¶
func (skv *ShoppingTxnKVStore) CartAddItem(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
CartAddItem is the subruntine as a part of txn, to add or discard some items to a specific cart.
func (*ShoppingTxnKVStore) CartExist ¶
func (skv *ShoppingTxnKVStore) CartExist(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
CartExist is the subruntine as a part of txn, to check whether the cart exists.
func (*ShoppingTxnKVStore) ItemsStockMinus ¶
func (skv *ShoppingTxnKVStore) ItemsStockMinus(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
ItemsStockMinus is the subruntine as a part of txn, to minus the stock of one item when a order is made.
The subruntine is executed in a broadcast way, i.e. all the shards will execute it.
func (*ShoppingTxnKVStore) OrderRecord ¶
func (skv *ShoppingTxnKVStore) OrderRecord(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
OrderRecord is the subruntine as a part of txn, to record the order when a order is made.
func (*ShoppingTxnKVStore) PayAdd ¶
func (skv *ShoppingTxnKVStore) PayAdd(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
PayAdd is the subruntine as a part of txn, to increase the root's balance when a order is paid.
func (*ShoppingTxnKVStore) PayMinus ¶
func (skv *ShoppingTxnKVStore) PayMinus(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
PayMinus is the subruntine as a part of txn, to decrease the user's balance when a order is paid.
func (*ShoppingTxnKVStore) PayRecord ¶
func (skv *ShoppingTxnKVStore) PayRecord(initRet interface{}) (errCode int, rbf twopc.Rollbacker)
PayRecord is the subruntine as a part of txn, to record the payment when a order is paid.
type ShoppingTxnKVStoreService ¶
type ShoppingTxnKVStoreService struct { *ShoppingTxnKVStore // contains filtered or unexported fields }
ShoppingTxnKVStoreService is the service wrapping the ShoppingTxnKVStore and one partipant of 2pc.
func NewShoppingTxnKVStoreService ¶
func NewShoppingTxnKVStoreService(network, addr, coordAddr string) *ShoppingTxnKVStoreService
NewShoppingTxnKVStoreService starts the transaction-enabled kvstore.
func (*ShoppingTxnKVStoreService) Serve ¶
func (service *ShoppingTxnKVStoreService) Serve()
Serve starts the KV-store service.
type SubmitOrderArgs ¶
SubmitOrderArgs is the args for the SubmitOrder txn.
type SubmitOrderTxnInitArgs ¶
type SubmitOrderTxnInitArgs struct { OrderKey string CartIDStr string CartKey string // contains filtered or unexported fields }
SubmitOrderTxnInitArgs is intial args for SubmitOrder txn's initialization.
type SubmitOrderTxnInitRet ¶
type SubmitOrderTxnInitRet struct { SubmitOrderTxnInitArgs CartValue string Price int }
SubmitOrderTxnInitRet is return value for SubmitOrder txn's initialization.
type TxnTask ¶
type TxnTask struct {
// contains filtered or unexported fields
}
TxnTask records a txn and its initial args and return code.