inventory_manager
=================
inventory_manager, for flash sale inventory. by sqlalchemy and redis.
0.0.9.dev0 is ok
pip3 install inventory_manager
使用方法如下
.. code:: python
# InventoryManager 的原理如下,这个库,会利用 sqlalchemy 和 redis 来记录库存。
# 其中,sqlalchemy 会把设置库存操作记录到数据库的一个表中,然后数量同步记录到redis,
# 使用消费库存的时候,会在 redis 中执行 incr 或者 decr 操作,同时在数据库的另一个表中记录流水日志。
# 在 InventoryManager 中,会使用 python 动态创建 class 来实现 sqlalchemy 的 model class 定义和使用。
# 所以 InventoryManager 实例化的参数需要 sqlalchemy 的 db_session, Base,还需要 redis client。
# 考虑到实际使用场景,如果有多个 InventoryManager(), 那么他们的数据库表名字,redis 的 key,都需要用不同前缀隔开。
# 所以实例化的参数需要提供一个 prefix(要求 prefix 在业务中务必是 unique)
# demo 如下
# ========================================================================
import redis
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from inventory_manager import InventoryManager
# 创建必要的参数
# --------------------------------------------------
db_prefix = 'prefix'
# 这个 get_db_arg 是获取 sqlalchemy 的 db_session, Base,给 InventoryManager 参数用
def get_db_arg():
# 需要提前确认 database 已存在
db_config = 'mysql+pymysql://***:*****@127.0.0.1:3306/demo1?charset=utf8mb4'
echo = False
# echo = True
max_overflow = 1000
engine = create_engine(db_config, pool_size=40, max_overflow=max_overflow, pool_recycle=28000, pool_pre_ping=True, convert_unicode=True, echo=echo)
db_session = scoped_session(
sessionmaker(
autocommit=False,
autoflush=False,
bind=engine
)
)
Base = declarative_base()
Base.query = db_session.query_property()
Base.__table_args__ = {
'mysql_collate': 'utf8mb4_unicode_ci'
}
return db_session, Base
redis_host = '127.0.0.1'
redis_port = 6379
redis_db = 1
redis_password = ''
red = redis.StrictRedis(
host=redis_host,
port=redis_port,
password=redis_password,
db=redis_db,
decode_responses=True,
)
# red.flushdb()
db_session, db_Base = get_db_arg()
# 这个是例子,如 item_id='1'的初始库存10个,item_id='2'的初始库存20个,。。。。
map_sku_inventory = {
'1': 10,
'2': 20,
'3': 30,
}
# 以下是使用方法教程。
# ========================================================================
# ========================================================================
# ========================================================================
# ========================================================================
# 实例一个 InventoryManager,参数第一个代表字符串前缀,接着是前文的 sqlalchemy 变量,最后一个是 redis client
mgr = InventoryManager(db_prefix, db_session, db_Base, red)
# ping 一下 database 和 redis 通不通
mgr.ping()
# 这个会在 db 中创建对应的 table
mgr.create_table()
# 检查 db 中创建对应的 table 是否完成
mgr.table_initialized()
# 初始化设置库存最大数量
for item_id, num in map_sku_inventory.items():
mgr.inventory_init(item_id, num)
# 修改总库存量
mgr.modification_adjust(item_id='1', delta=100)
# 获取库存
r = mgr.get(item_id='1')
print('''r = mgr.get(item_id='1')''', r)
# 扣库存
success = mgr.usage_decr(item_id='1', num=1)
assert success
r = mgr.get(item_id='1')
print('''mgr.usage_decr(item_id='1', num=1)''', r)
# 扣库存
success = mgr.usage_decr(item_id='1', num=3)
assert success
r = mgr.get(item_id='1')
print('''mgr.usage_decr(item_id='1', num=3)''', r)
# 扣库存
success = mgr.usage_decr(item_id='1', num=999999999)
assert not success
r = mgr.get(item_id='1')
print('''failed, mgr.usage_decr(item_id='1', num=999999999)''', r)
# 退回库存
mgr.usage_incr(item_id='1', num=1000)
r = mgr.get(item_id='1')
print('''mgr.usage_incr(item_id='1', num=1000)''', r)
# redis 数据丢失或不一致的情况下,使用这个恢复 redis
r = mgr.refresh(item_id='1')
print('''r = mgr.refresh(item_id='1')''', r)
r = mgr.refresh_all()
'''
# todo
1. 幂等,幂等id
2. refresh() 没有加锁
done!!!! 3. def refresh_all()
4. 没测 flask_sqlalchemy 环境
5. 没测 sqlalchemy 2.x.x 兼容
6. table add column create_time
'''
Raw data
{
"_id": null,
"home_page": "https://github.com/ttm1234/inventory-manager",
"name": "inventory-manager",
"maintainer": "",
"docs_url": null,
"requires_python": "",
"maintainer_email": "",
"keywords": "inventory inventory_manager inventory-manager",
"author": "ttm1234",
"author_email": "",
"download_url": "",
"platform": null,
"description": "inventory_manager\n=================\n\ninventory_manager, for flash sale inventory. by sqlalchemy and redis.\n\n0.0.9.dev0 is ok\n\npip3 install inventory_manager\n\n\u4f7f\u7528\u65b9\u6cd5\u5982\u4e0b\n\n.. code:: python\n\n # InventoryManager \u7684\u539f\u7406\u5982\u4e0b\uff0c\u8fd9\u4e2a\u5e93\uff0c\u4f1a\u5229\u7528 sqlalchemy \u548c redis \u6765\u8bb0\u5f55\u5e93\u5b58\u3002\n # \u5176\u4e2d\uff0csqlalchemy \u4f1a\u628a\u8bbe\u7f6e\u5e93\u5b58\u64cd\u4f5c\u8bb0\u5f55\u5230\u6570\u636e\u5e93\u7684\u4e00\u4e2a\u8868\u4e2d\uff0c\u7136\u540e\u6570\u91cf\u540c\u6b65\u8bb0\u5f55\u5230redis\uff0c\n # \u4f7f\u7528\u6d88\u8d39\u5e93\u5b58\u7684\u65f6\u5019\uff0c\u4f1a\u5728 redis \u4e2d\u6267\u884c incr \u6216\u8005 decr \u64cd\u4f5c\uff0c\u540c\u65f6\u5728\u6570\u636e\u5e93\u7684\u53e6\u4e00\u4e2a\u8868\u4e2d\u8bb0\u5f55\u6d41\u6c34\u65e5\u5fd7\u3002\n # \u5728 InventoryManager \u4e2d\uff0c\u4f1a\u4f7f\u7528 python \u52a8\u6001\u521b\u5efa class \u6765\u5b9e\u73b0 sqlalchemy \u7684 model class \u5b9a\u4e49\u548c\u4f7f\u7528\u3002\n # \u6240\u4ee5 InventoryManager \u5b9e\u4f8b\u5316\u7684\u53c2\u6570\u9700\u8981 sqlalchemy \u7684 db_session, Base\uff0c\u8fd8\u9700\u8981 redis client\u3002\n # \u8003\u8651\u5230\u5b9e\u9645\u4f7f\u7528\u573a\u666f\uff0c\u5982\u679c\u6709\u591a\u4e2a InventoryManager(), \u90a3\u4e48\u4ed6\u4eec\u7684\u6570\u636e\u5e93\u8868\u540d\u5b57\uff0credis \u7684 key\uff0c\u90fd\u9700\u8981\u7528\u4e0d\u540c\u524d\u7f00\u9694\u5f00\u3002\n # \u6240\u4ee5\u5b9e\u4f8b\u5316\u7684\u53c2\u6570\u9700\u8981\u63d0\u4f9b\u4e00\u4e2a prefix\uff08\u8981\u6c42 prefix \u5728\u4e1a\u52a1\u4e2d\u52a1\u5fc5\u662f unique\uff09\n\n\n # demo \u5982\u4e0b\n # ========================================================================\n import redis\n from sqlalchemy import create_engine\n from sqlalchemy.orm import scoped_session, sessionmaker\n from sqlalchemy.ext.declarative import declarative_base\n\n from inventory_manager import InventoryManager\n\n\n # \u521b\u5efa\u5fc5\u8981\u7684\u53c2\u6570\n # --------------------------------------------------\n db_prefix = 'prefix'\n\n\n # \u8fd9\u4e2a get_db_arg \u662f\u83b7\u53d6 sqlalchemy \u7684 db_session, Base\uff0c\u7ed9 InventoryManager \u53c2\u6570\u7528\n def get_db_arg():\n # \u9700\u8981\u63d0\u524d\u786e\u8ba4 database \u5df2\u5b58\u5728\n db_config = 'mysql+pymysql://***:*****@127.0.0.1:3306/demo1?charset=utf8mb4'\n\n echo = False\n # echo = True\n max_overflow = 1000\n engine = create_engine(db_config, pool_size=40, max_overflow=max_overflow, pool_recycle=28000, pool_pre_ping=True, convert_unicode=True, echo=echo)\n\n db_session = scoped_session(\n sessionmaker(\n autocommit=False,\n autoflush=False,\n bind=engine\n )\n )\n Base = declarative_base()\n Base.query = db_session.query_property()\n Base.__table_args__ = {\n 'mysql_collate': 'utf8mb4_unicode_ci'\n }\n\n return db_session, Base\n\n\n redis_host = '127.0.0.1'\n redis_port = 6379\n redis_db = 1\n redis_password = ''\n\n red = redis.StrictRedis(\n host=redis_host,\n port=redis_port,\n password=redis_password,\n db=redis_db,\n\n decode_responses=True,\n )\n\n # red.flushdb()\n\n db_session, db_Base = get_db_arg()\n\n # \u8fd9\u4e2a\u662f\u4f8b\u5b50\uff0c\u5982 item_id='1'\u7684\u521d\u59cb\u5e93\u5b5810\u4e2a\uff0citem_id='2'\u7684\u521d\u59cb\u5e93\u5b5820\u4e2a\uff0c\u3002\u3002\u3002\u3002\n map_sku_inventory = {\n '1': 10,\n '2': 20,\n '3': 30,\n }\n\n\n # \u4ee5\u4e0b\u662f\u4f7f\u7528\u65b9\u6cd5\u6559\u7a0b\u3002\n # ========================================================================\n # ========================================================================\n # ========================================================================\n # ========================================================================\n\n # \u5b9e\u4f8b\u4e00\u4e2a InventoryManager\uff0c\u53c2\u6570\u7b2c\u4e00\u4e2a\u4ee3\u8868\u5b57\u7b26\u4e32\u524d\u7f00\uff0c\u63a5\u7740\u662f\u524d\u6587\u7684 sqlalchemy \u53d8\u91cf\uff0c\u6700\u540e\u4e00\u4e2a\u662f redis client\n mgr = InventoryManager(db_prefix, db_session, db_Base, red)\n\n # ping \u4e00\u4e0b database \u548c redis \u901a\u4e0d\u901a\n mgr.ping()\n\n # \u8fd9\u4e2a\u4f1a\u5728 db \u4e2d\u521b\u5efa\u5bf9\u5e94\u7684 table\n mgr.create_table()\n\n # \u68c0\u67e5 db \u4e2d\u521b\u5efa\u5bf9\u5e94\u7684 table \u662f\u5426\u5b8c\u6210\n mgr.table_initialized()\n\n # \u521d\u59cb\u5316\u8bbe\u7f6e\u5e93\u5b58\u6700\u5927\u6570\u91cf\n for item_id, num in map_sku_inventory.items():\n mgr.inventory_init(item_id, num)\n\n # \u4fee\u6539\u603b\u5e93\u5b58\u91cf\n mgr.modification_adjust(item_id='1', delta=100)\n\n # \u83b7\u53d6\u5e93\u5b58\n r = mgr.get(item_id='1')\n print('''r = mgr.get(item_id='1')''', r)\n\n # \u6263\u5e93\u5b58\n success = mgr.usage_decr(item_id='1', num=1)\n assert success\n r = mgr.get(item_id='1')\n print('''mgr.usage_decr(item_id='1', num=1)''', r)\n\n # \u6263\u5e93\u5b58\n success = mgr.usage_decr(item_id='1', num=3)\n assert success\n r = mgr.get(item_id='1')\n print('''mgr.usage_decr(item_id='1', num=3)''', r)\n\n # \u6263\u5e93\u5b58\n success = mgr.usage_decr(item_id='1', num=999999999)\n assert not success\n r = mgr.get(item_id='1')\n print('''failed, mgr.usage_decr(item_id='1', num=999999999)''', r)\n\n # \u9000\u56de\u5e93\u5b58\n mgr.usage_incr(item_id='1', num=1000)\n r = mgr.get(item_id='1')\n print('''mgr.usage_incr(item_id='1', num=1000)''', r)\n\n # redis \u6570\u636e\u4e22\u5931\u6216\u4e0d\u4e00\u81f4\u7684\u60c5\u51b5\u4e0b\uff0c\u4f7f\u7528\u8fd9\u4e2a\u6062\u590d redis\n r = mgr.refresh(item_id='1')\n print('''r = mgr.refresh(item_id='1')''', r)\n\n r = mgr.refresh_all()\n\n '''\n # todo \n 1. \u5e42\u7b49\uff0c\u5e42\u7b49id\n 2. refresh() \u6ca1\u6709\u52a0\u9501\n done!!!! 3. def refresh_all()\n 4. \u6ca1\u6d4b flask_sqlalchemy \u73af\u5883\n 5. \u6ca1\u6d4b sqlalchemy 2.x.x \u517c\u5bb9\n 6. table add column create_time\n '''\n",
"bugtrack_url": null,
"license": "",
"summary": "inventory manager, for flash sale inventory. by sqlalchemy and redis.",
"version": "0.0.9.dev0",
"project_urls": {
"Homepage": "https://github.com/ttm1234/inventory-manager"
},
"split_keywords": [
"inventory",
"inventory_manager",
"inventory-manager"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "1aca79ae9fd47a68720423abe2ca973038e668bfed69f12f4b80bebdd1b5f6c0",
"md5": "7ece2bd9907d77d63a3d5147f414e222",
"sha256": "d35b763b49657b2c36a59d5cfa7a76c89b4a1d5bc199e9ecd79a8edf2062a089"
},
"downloads": -1,
"filename": "inventory_manager-0.0.9.dev0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "7ece2bd9907d77d63a3d5147f414e222",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 10649,
"upload_time": "2024-01-26T14:14:11",
"upload_time_iso_8601": "2024-01-26T14:14:11.667966Z",
"url": "https://files.pythonhosted.org/packages/1a/ca/79ae9fd47a68720423abe2ca973038e668bfed69f12f4b80bebdd1b5f6c0/inventory_manager-0.0.9.dev0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-01-26 14:14:11",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ttm1234",
"github_project": "inventory-manager",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [],
"lcname": "inventory-manager"
}