레일간에 여러 데이터베이스 연결 풀을 사용하여 전환 할 수 있습니까?


12

작은 배경

나는 수년간 멀티 테넌시 앱을 실행하기 위해 Apartment gem 을 사용해 왔습니다. 이제는 데이터베이스를 별도의 호스트로 확장해야 할 필요성에 도달했으며 DB 서버는 더 이상 유지할 수 없습니다 (읽기와 쓰기가 너무 많이 걸리는 경우). 그렇습니다. 하드웨어를 최대로 확장했습니다 (전용 하드웨어, 64 코어, RAID 10의 12 Nvm-e 드라이브, 384Gb 램 등).

이 테넌트 별 (1 테넌트 = 1 데이터베이스 연결 구성 / 풀)을 수행하는 것을 고려하고 number-of-tenants있었습니다. 이는 애플리케이션 코드 변경 작업을 수행하지 않고도 용량을 최대로 늘릴 수있는 "간단하고"효율적인 방법이기 때문 입니다.

이제 레일 4.2 atm.을 실행 중이며 곧 5.2로 업그레이드하고 있습니다. Rails 6은 모델 별 연결 정의에 대한 지원을 추가하지만 20 개 테넌트 각각에 대해 완전히 미러링 된 데이터베이스 스키마를 가지고 있기 때문에 실제로 필요한 것은 아닙니다. 일반적으로 요청 (미들웨어) 또는 백그라운드 작업 (sidekiq 미들웨어) 당 "데이터베이스"를 전환하지만, 이는 search_pathPostgresql에서 설정 하고 실제 연결을 실제로 변경하지 않기 때문에 현재는 사소하고 처리 되지 않습니다. 테넌트 호스팅 전략으로 전환 할 때 요청마다 전체 연결을 전환해야합니다.

질문 :

  1. ActiveRecord::Base.establish_connection(config)요청 / 백그라운드 작업을 수행 할 수 있다는 것을 알고 있습니다. 그러나 또한 완전히 새로운 데이터베이스 연결 핸드 셰이크가 발생하고 새로운 DB 풀이 레일에 생성됩니다. 내 응용 프로그램에 대한 모든 단일 요청에서 이러한 종류의 오버 헤드를 만드는 성능 자살이 될 것입니다.
  2. 따라서 누구나 처음부터 (예 : 응용 프로그램 부팅시) 여러 (총 20 개의) 데이터베이스 연결 / 풀을 사전 설정 한 레일을 사용하여 옵션을 볼 수 있는지 궁금한 경우 요청 당 해당 풀 사이를 전환하면됩니까? 따라서 db 연결이 이미 작성되었으며 사용할 준비가되었습니다.
  3. 이 모든 것이 나쁜 나쁜 생각일까요? 대신 다른 접근법을 찾아야합니까? 예 : 하나의 앱 인스턴스 = 하나의 특정 테넌트에 대한 하나의 특정 연결. 또는 다른 것.


1
최근에 Rails 지점에 필요한 기능을 정확히 추가 한 Rails의 GitHub 리포지토리 에서이 PR에 관심이있을 수 있습니다 master. Rails Egde를 실행하는 것이 현재 Rails 버전에 대한 옵션입니까?
spickermann

@spickermann ActiveRecord::Base.connected_to(shard: :shard_one) do ... end은 매번 완전히 새로운 연결을 만드는 대신 풀을 다시 사용한다는 것을 의미합니까?

답변:


4

내가 이해하는 것처럼 멀티 테넌시 앱에는 4 가지 패턴이 있습니다.

1. 전용 모델 / 다중 프로덕션 환경

각 인스턴스 또는 데이터베이스 인스턴스는 완전히 다른 테넌트 애플리케이션을 호스팅하며 테넌트간에 공유되는 것은 없습니다.

1 명의 테넌트를위한 1 개의 인스턴스 앱과 1 개의 데이터베이스입니다. 1 명의 테넌트 만 서비스하는 것처럼 개발이 쉬울 것입니다. 그러나 100 명의 임차인이 있다면 devops에게는 악몽이 될 것입니다.

2. 임차인의 물리적 분리

모든 테넌트 용 인스턴스 앱 1 개, 테넌트 1 개용 데이터베이스 1 개 이것은 당신이 찾고있는 것입니다. 당신은 사용할 수 있습니다 ActiveRecord::Base.establish_connection(config), 또는 기타 알 수 있듯이 레일 6 보석, 또는 업데이트를 사용. 아래 (2)에 대한 답변을 참조하십시오.

3. 분리 된 스키마 모델 / 논리적 분리

격리 된 스키마에서 테넌트 테이블 또는 데이터베이스 구성 요소는 논리적 스키마 또는 네임 스페이스로 그룹화되고 다른 테넌트 스키마와 분리되지만 스키마는 동일한 데이터베이스 인스턴스에서 호스팅됩니다.

아파트 보석과 마찬가지로 모든 테넌트에 대한 1 개의 인스턴스 앱과 1 개의 데이터베이스.

4. 부분적으로 고립 된 구성 요소

이 모델에서 공통 기능을 가진 구성 요소는 테넌트간에 공유되며 고유하거나 관련없는 기능을 가진 구성 요소는 격리됩니다. 데이터 계층에서 테넌트를 식별하는 데이터와 같은 공통 데이터는 단일 테이블로 그룹화되거나 유지되는 반면 테넌트 특정 데이터는 테이블 또는 인스턴스 계층에서 격리됩니다.


(1) ActiveRecord::Base.establish_connection(config)은 요청에 따라 DB별로 핸드 쉐이킹하지 마십시오. 여기에서 확인 하고 모든 의견을 읽을 수 있습니다 .

(2)와 같이 사용하지 않으려면 establish_connectiongem multiverse (rails 4.2에서 작동) 또는 다른 gem을 사용할 수 있습니다. 또는 다른 제안처럼 Rails 6으로 업데이트 할 수 있습니다.

편집 : Multiverse gem이 사용하고 establish_connection있습니다. 를 추가하고 database.yml각 서브 클래스가 동일한 연결 / 풀을 공유하도록 기본 클래스를 작성합니다. 기본적으로 사용 노력이 줄어 듭니다 establish_connection.

(3)에 대한 답은 다음과 같습니다.

테넌트가 많지 않고 응용 프로그램이 매우 복잡한 경우 전용 모델 패턴을 사용하는 것이 좋습니다. 따라서 하나의 앱 인스턴스 = 하나의 특정 테넌트에 대한 하나의 특정 연결로 이동합니다. 여러 데이터베이스 연결을 추가하여 앱을 더 복잡하게 만들 필요는 없습니다.

그러나 테넌트가 많은 경우 테넌트의 물리적 분리 또는 부분적으로 격리 된 구성 요소를 사용하는 것이 비즈니스 프로세스에 따라 다릅니다.

어느 쪽이든, 새로운 아키텍처를 준수하려면 애플리케이션을 업데이트 / 다시 작성해야합니다.


답변 주셔서 감사합니다. 현상금이 좋은 해결책이라면 현상금 중 하나를 보상하기 전에 제안을 실제로 테스트하는 데 약간의 시간이 필요합니다.
Niels Kristian

1과 2와 관련하여 몇 가지 질문이 있습니다. 1 : 귀하의 언급을 이해하지 못했습니다. db 핸드 셰이크를 수행하거나 db 폴을 다시 만들지 않고 .establish_connection (config)을 호출 할 수 있다고 말하는 것입니까? 이 경우 두 링크가 어떻게 그것을 설명하는지 잘 모르겠습니다. 2 : multiverse의 경우 전체 앱의 전체 db 스위치가 아니라 모델 별 데이터베이스 전환이 아닌가? 나는 그들의 문서가 아주 모호하다고 생각한다
Niels Kristian

오해가 있다고 생각합니다. 이 문장들을 자세히 설명 하시겠습니까? 내가 요청 / 백그라운드 작업 당 액티브 :: Base.establish_connection (설정)을 할 수 있음을 이해 - 그러나, 나는 또한 이해, 레일의 산란에 완전히 새로운 데이터베이스 연결 핸드 셰이크가 만들어 질 것을 트리거 및 새로운 DB 풀 이 하나의 요청으로 하나의 DB 풀을 생성하도록 제안 하시겠습니까?
KSD Putra

의미 : (1) 모든 요청에 ​​대해 ActiveRecord :: Base.establish_connection (config)를 호출 할 때 성능 / 네트워크 오버 헤드가 걱정됩니다. 다른 데이터베이스 / 국가 간을 전환하기 만하면됩니다.
Niels Kristian

오버 헤드에 대해 걱정할 필요가 없습니다. 이제 단일 DB를 사용하는 경우 하나의 연결 풀이 있습니다 (위의 (1)의 답변에서 연결에 대한 링크를 확인할 수 있음). 다음 establish_connection과 같은 모델에서 사용 하고 class SecondTenantUser < ActiveRecord::Base; establish_connection(DB_SECOND_TENANT); end5 개의 모델이 있다고 가정하면 DB_SECOND_TENANT에 5 개의 연결 풀을 작성합니다. 그리고 각 수영장은 똑같이 취급됩니다. 따라서 요청별로 풀을 만들지 않고마다 풀을 만듭니다 establish_connection.
KSD 푸트 라

3

내가 이해 한 바에 따르면 (2) Rails 6의 수동 연결 전환 을 통해 가능해야합니다 .


그러나 고맙지 만 이것은 내 유스 케이스와는 거리가 멀다. 이 절차를 어디에서나 사용하기 위해 전체 앱을 다시 작성한다는 의미입니다.
Niels Kristian

3

며칠 전에 GitHub의 Ruby on Rails 지점에 수평 샤딩 이 추가되었습니다 master. 현재이 기능은 공식적으로 출시되지는 않지만 애플리케이션의 Rails 버전에 따라 master다음을 추가 하여 Rails 사용을 고려할 수 있습니다 Gemfile.

gem "rails", github: "rails/rails", branch: "master"

이 새로운 기능을 사용하면 Rails의 데이터베이스 연결 풀을 활용하고 조건에 따라 데이터베이스를 전환 할 수 있습니다.

이 새로운 기능을 사용하지는 않았지만 매우 간단합니다.

# in your config/database.yml
production:
  primary:
    database: my_database
    # other config: user, password, etc
  primary_tenant_1:
    database: tenant_1_database
    # other config: user, password, etc

# in your controller for example when updating a tenant
ActiveRecord::Base.connected_to(shard: "primary_tenant_#{tenant.database_shard_number}") do
  tenant.save
end

테넌트 번호를 결정하는 방법 또는 응용 프로그램에서 권한 부여가 수행되는 방법에 대해서는 자세히 설명하지 않았습니다. 그러나에서 가능한 한 빨리 임차인 번호를 결정하려고 application_controller합니다 around_action. 이와 같은 것이 시작점이 될 수 있습니다.

around_filter :determine_database_connection

private

def determine_database_connection
  # assuming you have a method to determine the current_tenant and that tenant
  # has a method that returns the number of the shard to use or even the 
  # full shard identifier
  shard = current_tenant.database_shard # returns for example `:primary_tenant_1` 

  ActiveRecord::Base.connected_to(shard: shard) do
    yield
  end
end

이 경우에도 기본 연결로 다시 전환하는 것이 똑같은 의미입니까? github.com/influitive/apartment#middleware-considerations
Ben

1
ActiveRecord::Base.connected_to ... do블록 을 나가면 기본 연결이 다시 사용됩니다.
spickermann

@ spickermann 난이 보석을 읽고 있었어요, rails6뿐만 아니라?
7urkm3n

@ 7urkm3n 현재 Rails master지점에 포함되어 있습니다.
spickermann

답변 주셔서 감사합니다. 현상금이 좋은 해결책이라면 현상금 중 하나를 보상하기 전에 제안을 실제로 테스트하는 데 약간의 시간이 필요합니다.
Niels Kristian
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.