用ActiveRecord和CockroachDB构建Rails应用程序的详细指南

81 阅读4分钟

嘉宾帖子提醒Marla和Ali与Cockroach Labs团队合作,让ActiveRecord CockroachDB适配器为Rails 5.2及以后的版本做好准备!他们与Cockroach Labs的合作已经结束,但适配器仍然存在。他们与Cockroach Labs的合作已经结束,但这个适配器却一直存在。这篇博文最初是在他们的博客Test Double.上分享的。

当我在Rails应用中工作时,我发现自己并不太担心数据库的问题。由于Rails原生支持MySQL和PostgreSQL等流行的数据库,我通常只需要做一些配置上的改变,就可以让应用程序的数据库启动和运行。我也没有发现自己在使用Rails不支持的数据库时遇到太多问题。由于Rails有良好的数据库接口文档和强大的社区支持,我仍然只需要做一些配置修改就可以使用OracleSQL Server等数据库。

如果你同意我的观点,那么在Rails中使用CockroachDB也就不足为奇了!🎉

什么是CockroachDB?

很高兴你问到了!CockroachDB是由Cockroach Labs建立的,是一个被设计为可扩展和高度可用的数据库。它还使用了PostgreSQL的线程协议,所以你几乎可以在任何你可以使用PostgreSQL的地方使用它。几乎是这样(后面会有更多的介绍)。

那么我们如何在Rails中使用CockroachDB呢?因为我喜欢通过实例来学习,所以让我们配置一个现有的Rails应用来使用CockroachDB。

在Rails中使用CockroachDB

在这个例子中,我们将改变CodeTriage Rails应用,使其使用CockroachDB而不是PostgreSQL。

在按照CodeTriage的贡献指南让应用在本地运行后,该应用就可以与PostgreSQL对话了。

要切换到使用CockroachDB,我们首先需要安装和配置CockroachDB。

如何安装CockroachDB?

首先,按照安装指南安装CockroachDB。接下来,我们将使用cockroach demo命令来创建一个单节点的CockroachDB集群。我们将使用--empty 标志运行该命令,这样我们就不会在以后加载CodeTriage模式时遇到任何冲突。

$ cockroach demo --empty
#
# Welcome to the CockroachDB demo database!
#
# You are connected to a temporary, in-memory CockroachDB cluster of 1 node.
#
# This demo session will attempt to enable enterprise features
# by acquiring a temporary license from Cockroach Labs in the background.
# To disable this behavior, set the environment variable
# COCKROACH_SKIP_ENABLING_DIAGNOSTIC_REPORTING=true.
#
# Reminder: your changes to data stored in the demo session will not be saved!
#
# Connection parameters:
#   (console) http://127.0.0.1:63115
#   (sql)     postgres://root:admin@?host=%2Fvar%2Ffolders%2Fzj%2F41x2d76s4kq4vv8_c8qrl1z00000gn%2FT%2Fdemo900101820&port=26257
#   (sql/tcp) postgres://root:admin@127.0.0.1:63117?sslmode=require
#
#
# The user "root" with password "admin" has been created. Use it to access the Web UI!
#
# Server version: CockroachDB CCL v20.2.5 (x86_64-apple-darwin14, built 2021/02/16 12:57:34, go1.13.14) (same version as client)
# Cluster ID: 83ec1cc1-4b7a-410f-b0b4-dea5ea562b9b
#
# Enter \? for a brief introduction.
#
root@127.0.0.1:63117/defaultdb>

cockroach demo 命令创建了空的数据库之后,它打开了一个交互式SQL shell。当shell打开时,演示数据库只存在于内存中,所以我们将保持它的开放,直到我们完成。

cockroach demo 命令也给了我们一些关于如何连接它的信息。

# Connection parameters:
#   (console) http://127.0.0.1:63115
#   (sql)     postgres://root:admin@?host=%2Fvar%2Ffolders%2Fzj%2F41x2d76s4kq4vv8_c8qrl1z00000gn%2FT%2Fdemo900101820&port=26257
#   (sql/tcp) postgres://root:admin@127.0.0.1:63117?sslmode=require

从这些信息中我们可以看到

  1. 我们有一个名为root 的用户,他的密码是admin
  2. CockroachDB服务器正在监听127.0.0.1 (又名localhost ),端口为63117
  3. sslmode 被设置为require

当你运行cockroach demo 命令时,这些细节大多是相同的,但端口可能不同。

请注意这些连接细节,因为我们以后会需要它们。

现在CockroachDB已经在本地启动并运行,我们准备对CodeTriage进行一些配置上的修改。

添加ActiveRecord CockroachDB适配器

首先,我们要编辑Gemfile ,用ActiveRecord CockroachDB Adapter gem替换pg gem。由于CodeTriage目前是针对Rails 6.1运行的,我们将安装ActiveRecord CockroachDB Adapter的v6.1.0.beta1版本。

--- a/Gemfile
+++ b/Gemfile
@@ -31,7 +31,7 @@ gem 'local_time', '2.1.0'
 gem 'maildown', '~> 3.1'
 gem 'omniauth', '~> 1.9.1'
 gem 'omniauth-github'
-gem 'pg'
+gem 'activerecord-cockroachdb-adapter', '6.1.0beta1'
 gem 'puma'
 gem 'rack-timeout'
 gem 'rrrretry'

然后,在用bundle install 安装 gem 后,我们将对config/database.yml 进行一些修改。

配置CodeTriage以使用ActiveRecord CockroachDB适配器

首先,我们将把adapter 的值从postgresql 改为cockroachdb

--- a/config/database.yml
+++ b/config/database.yml
@@ -1,5 +1,5 @@
 defaults: &defaults
-  adapter: postgresql
+  adapter: cockroachdb
   encoding: utf8
   pool: 5
   host: localhost

接下来,我们将从CockroachDB的交互式SQL shell中获取我们前面提到的连接细节

  1. 我们有一个名为root 的用户,他的密码是admin
  2. CockroachDB服务器正在监听127.0.0.1 (又名localhost ),端口为63117
  3. 并且sslmode 被设置为require

并设置port,user,password, 和requiressl

--- a/config/database.yml
+++ b/config/database.yml
@@ -3,7 +3,10 @@ defaults: &defaults
   encoding: utf8
   pool: 5
   host: localhost
-  password:
+  port: 63117
+  user: root
+  password: admin
+  requiressl: true

现在CodeTriage应该可以使用CockroachDB了!让我们通过运行bin/rake db:create db:schema:load db:seed 来设置数据库。

$ bin/rake db:create db:schema:load db:seed
Created database 'triage_development'
Created database 'triage_test'
rake aborted!
ActiveRecord::StatementInvalid: PG::FeatureNotSupported: ERROR:  unimplemented: extension "pg_stat_statements" is not yet supported
HINT:  You have attempted to use a feature that is not yet implemented.
See: https://go.crdb.dev/issue-v/54516/v20.2
/Users/alimi/.rvm/gems/ruby-2.7.2/gems/activerecord-6.1.0/lib/active_record/connection_adapters/postgresql_adapter.rb:678:in `exec_params'
/Users/alimi/.rvm/gems/ruby-2.7.2/gems/activerecord-6.1.0/lib/active_record/connection_adapters/postgresql_adapter.rb:678:in `block (2 levels) in exec_no_cache'
/Users/alimi/.rvm/gems/ruby-2.7.2/gems/activesupport-6.1.0/lib/active_support/dependencies/interlock.rb:48:in `block in permit_concurrent_loads'

呃......这看起来不妙。😅

CockroachDB听起来像PostgreSQL,但它不是PostgreSQL

如果我们再看一下最后一条命令/错误,我们可以看到CodeTriage数据库是在CockroachDB中创建的。

$ bin/rake db:create db:schema:load db:seed
Created database 'triage_development'
Created database 'triage_test'

但是当试图从db/schema.rb 中加载数据库模式时,事情就出错了。

rake aborted!
ActiveRecord::StatementInvalid: PG::FeatureNotSupported: ERROR:  unimplemented: extension "pg_stat_statements" is not yet supported
HINT:  You have attempted to use a feature that is not yet implemented.
See: https://go.crdb.dev/issue-v/54516/v20.2

db/schema.rb ,CodeTriage正在启用pg_stat_statements 扩展,但正如错误告诉我们的那样,CockroachDB不支持它。

尽管CockroachDB使用了PostgreSQL的线程协议,并且行为上很像PostgreSQL,但是记住CockroachDB不是PostgreSQL是非常重要的。你可以在很多地方像使用PostgreSQL一样使用CockroachDB,这意味着你不必为使用它而学习大量的新东西。但是你可能会遇到像这样的行为上的小差异。

为了演示,我们将改变CodeTriage的db/schema.rb ,使其不再启用pg_stat_statments 扩展(也不启用plpgsql 扩展)。

--- a/db/schema.rb
+++ b/db/schema.rb
@@ -12,9 +12,6 @@
 
 ActiveRecord::Schema.define(version: 2020_11_15_123025) do
 
-  # These are extensions that must be enabled in order to support this database
-  enable_extension "pg_stat_statements"
-  enable_extension "plpgsql"
 
   create_table "data_dumps", id: :serial, force: :cascade do |t|
     t.text "data"

现在,让我们再次尝试加载模式和种子。

$ bin/rake db:schema:load db:seed
success
....................................................................................................%

好的,这看起来好多了。但我们真的可以不使用这些扩展吗?

CodeTriage会在它期望PostgreSQL扩展被安装和可用的地方出错。我们在这里不需要担心这个问题,因为这只是一篇博文,但如果我正在改变一个生产数据库,这将使我感到不安。如果这是一次真正的迁移,我会查看兼容性文档并更新应用程序,使其不再依赖PostgreSQL的功能。

现在我们已经完成了配置的修改,并设置了数据库,我们应该可以从CodeTriage中与CockroachDB对话了。🕺🏾

从CodeTriage连接到CockroachDB数据库

让我们启动一个rails console ,获取一些数据吧!由于我们之前运行了bin/rake db:seed ,我们的数据库应该有一些种子数据。

$ bin/rails console
Loading development environment (Rails 6.1.0)
>> User.count
   (67.7ms)  SELECT COUNT(*) FROM "users"
=> 101

好的,我们有101个用户。让我们试着获取第一个用户。

>> User.first
  User Load (2.3ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]
  TRANSACTION (0.9ms)  BEGIN
  User Update (3.9ms)  UPDATE "users" SET "updated_at" = $1, "account_delete_token" = $2 WHERE "users"."id" = $3  [["updated_at", "2021-03-04 01:00:45.821404"], ["account_delete_token", "874464f621a5930c859c5b99b9d1d26705386d61bd34caf00b1288e949dec48dc257459c6fcb297da12ef32dd16419ff64bc7289d94425a94c46fd94ffb89ce9"], ["id", 637885482494296065]]
  TRANSACTION (22.5ms)  COMMIT
=> #<User id: 637885482494296065, email: "", created_at: "2021-03-03 02:13:20.466321000 +0000", updated_at: "2021-03-04 01:00:45.821404000 +0000", zip: nil, phone_number: nil, twitter: nil, github: "schneems", github_access_token: nil, admin: nil, avatar_url: "http://gravatar.com/avatar/default", name: nil, private: false, favorite_languages: nil, daily_issue_limit: 50, skip_issues_with_pr: false, account_delete_token: "874464f621a5930c859c5b99b9d1d26705386d61bd34caf00b...", last_clicked_at: "2021-03-03 02:13:20.466267000 +0000", email_frequency: "daily", email_time_of_day: nil, old_token: nil, raw_streak_count: 0, raw_emails_since_click: 0, last_email_at: nil>

它成功了!🙌🏾

你可能注意到这个用户有一个非常大的ID。CodeTriage为用户表指定了一个Serial id,所以你可能希望我们的第一个用户的id是1。 CockroachDB识别Serial,但它不是从1开始依次分配用户id,而是根据交易时间戳和节点的id来分配。CockroachDB这样做是为了确保全局唯一的ID在各节点之间以一种高性能的方式被使用。如果你忘记了,CockroachDB和PostgreSQL是不一样的。

我还可以通过运行bin/rails server ,并观察服务器的输出,看到ActiveRecord向CockroachDB进行了一些查询。

$ bin/rails s
=> Booting Puma
=> Rails 6.1.0 application starting in development
…
Started GET "/" for 127.0.0.1 at 2021-03-03 20:25:52 -0500
   (0.7ms)  SHOW crdb_version
   (3.5ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
Processing by PagesController#index as HTML
   (1.8ms)  SELECT COUNT(*) FROM "users"
  ↳ app/controllers/pages_controller.rb:59:in `block in description'
   (1.1ms)  SELECT COUNT(*) FROM "repos"
  ↳ app/controllers/pages_controller.rb:60:in `block in description'
…
  ↳ app/views/pages/_repos_with_pagination.html.slim:1
  Repo Load (2.7ms)  SELECT "repos"."id", "repos"."updated_at", "repos"."issues_count", "repos"."language", "repos"."full_name", "repos"."name", "repos"."description" FROM "repos" WHERE (issues_count > 0) ORDER BY issues_count DESC LIMIT $1 OFFSET $2  [["LIMIT", 50], ["OFFSET", 0]]
…
Completed 200 OK in 661ms (Views: 387.9ms | ActiveRecord: 226.8ms | Allocations: 117408)

这些查询是有效的!而应用程序也在加载!!!。

今天就在Rails中使用CockroachDB

感谢ActiveRecord CockroachDB适配器,我们可以像其他数据库一样在Rails应用中使用CockroachDB。由于CockroachDB与PostgreSQL的对话和行为非常相似,它几乎可以取代PostgreSQL(几乎是😉)。

今天就试试在你的Rails应用中使用CockroachDB吧!

Humblebrag:Marla和我与Cockroach Labs合作,让ActiveRecord CockroachDB适配器为Rails 5.2及以后的版本做好准备,这让我们感到非常开心。我们与Cockroach Labs的合作已经结束了,但这个适配器还在继续。在GitHub上关注Cockroach Labs的进展。