【问题标题】:How to make database connection switchable at runtime when using SQLC使用 SQLC 时如何使数据库连接在运行时可切换
【发布时间】:2021-08-06 08:38:31
【问题描述】:

我知道这个问题已经被问过很多次了,但我没有为我的案例找到一个好的答案。我正在使用 SQLC 生成查询数据库的方法。使用在开始时初始化的一个连接时一切正常。现在我需要在一个多租户环境中设置它,每个租户都有一个单独的数据库。现在,我想从连接租户与数据库连接的连接图 (map[string]*sql.DB) 开始。我的问题是关于在运行时覆盖/选择连接。通过一个连接,存储库的初始化如下:

type Repository interface {
    GetCustomerById(ctx context.Context, id int64) (Customer, error)
    ListCustomers(ctx context.Context) ([]Customer, error)
}

type repoSvc struct {
    *Queries
    db *sql.DB
}

func NewRepository(dbconn *sql.DB) Repository {
    return &repoSvc{
        Queries: New(dbconn),
        db:      dbconn,
    }
}

customerRepo := customerRepo.NewRepository(conn)

GetCustomerById 是 SQLC 生成的方法 conn 是数据库连接

如何根据参数(来自cookie或上下文)建立连接?

【问题讨论】:

    标签: database go multi-tenant


    【解决方案1】:

    假设您使用单独的数据库,最简单的方法是维护 map[tenantID]Repository,其中 tenantID 是您区分租户的方式(例如,包含租户 ID 的 stringuint)。

    这样你就可以在运行时做所有事情:

    • 当您需要添加租户时,只需为该租户实例化 Repository 并将其添加到地图中
    • 当您需要移除租户时,只需从地图中移除其Repository 并关闭数据库连接
    • 当您需要查询某个租户时,在地图中查找对应的Repository,并使用它对该租户进行查询

    如果上述操作可能同时发生,请确保在访问地图时使用某种同步机制来避免数据争用(例如sync.Mapsync.RWMutex)。

    如果您有一个存储租户及其数据库连接 URI 的数据库表,您仍然可以使用这种方法:当您需要执行查询时,检查 Repository 是否存在于 map 中:如果缺少,查询租户表并将该租户的Repository 添加到地图中。然后您可以定期扫描map 并删除任何一段时间未使用的Repository

    为了使这一切更容易,您还可以将整个机器包装到一个MultitenantRepository 接口中,该接口与Repository 接口相同,但在每个方法上都接受一个额外的tenantID 参数:

    type MultitenantRepository interface {
        GetCustomerById(ctx context.Context, tenant tenantID, id int64) (Customer, error)
        ListCustomers(ctx context.Context, tenant tenantID) ([]Customer, error)
    }
    

    这将避免将多租户设置的所有复杂性暴露给您的业务逻辑。

    【讨论】:

    • 所以您的建议是尽可能在最低层执行此操作。这也是我的想法。感谢您提供有用的答案!
    猜你喜欢
    • 2020-05-12
    • 1970-01-01
    • 2015-07-25
    • 2019-10-25
    • 2015-10-07
    • 1970-01-01
    • 2014-01-17
    • 1970-01-01
    • 2012-04-06
    相关资源
    最近更新 更多