Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use LMDB key-value store for I18n backend
* Use i18n fork for CacheFile module * Use LMDB for key-value store for loaded i18n translations * add test_lmdb_key_value unit test for simple coverage * Add I18n backend configuration to Cdo::I18nBackend * Configure i18n backend with CDO.i18n_key_value setting Default false in production environment during controlled roll-out.
- Loading branch information
Showing
8 changed files
with
120 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
require 'i18n' | ||
require 'active_support/core_ext/numeric/bytes' | ||
require 'cdo/lmdb_key_value' | ||
|
||
module Cdo | ||
# I18n backend instance used by the web application. | ||
class I18nBackend < ::I18n::Backend::KeyValue | ||
include ::I18n::Backend::CacheFile | ||
|
||
CACHE_DIR = pegasus_dir('cache', 'i18n/cache') | ||
|
||
# Maximum size of the i18n cache file. | ||
# Used to set the fixed memory-map size. | ||
MAX_CACHE_SIZE = 2.gigabytes | ||
|
||
def initialize | ||
store = LMDBKeyValue.new(CACHE_DIR, size: MAX_CACHE_SIZE) | ||
super(store, false) | ||
self.path_roots = [Gem.dir, deploy_dir] | ||
end | ||
|
||
alias init_translations load_translations | ||
alias reload! load_translations | ||
end | ||
end | ||
# Use custom i18n backend by enabling `CDO.i18n_key_value`. | ||
# Default false in production environment during controlled roll-out. | ||
CDO.i18n_backend = CDO.with_default(!rack_env?(:production)).i18n_key_value ? | ||
Cdo::I18nBackend.new : | ||
I18n::Backend::Simple.new |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
require 'lmdb' | ||
require 'fileutils' | ||
require 'digest' | ||
|
||
# Simple wrapper around LMDB to provide a key-value interface. | ||
# Use separate reader and writer objects to optimize the common read-only case. | ||
module Cdo | ||
class LMDBKeyValue | ||
def initialize(dir, size: 0) | ||
FileUtils.mkpath(dir) | ||
@dir = dir | ||
@options = { | ||
writemap: true, | ||
mapasync: true, | ||
nometasync: true, | ||
mapsize: size | ||
} | ||
end | ||
|
||
def reader | ||
return @reader if @reader | ||
@read_env = ::LMDB.new(@dir, @options.merge(rdonly: true)) | ||
@read_env.transaction(true) do | ||
@reader = @read_env.database | ||
end | ||
rescue LMDB::Error => e | ||
raise unless e.message == 'No such file or directory' | ||
# Create database with writer first, then reload read-only environment. | ||
writer | ||
retry | ||
end | ||
|
||
def writer | ||
return @writer if @writer | ||
@write_env = ::LMDB.new(@dir, @options) | ||
@writer = @write_env.database(nil, create: true) | ||
end | ||
|
||
def [](key) | ||
reader.get(digest(key)) | ||
end | ||
|
||
def []=(key, value) | ||
writer.put(digest(key), value) | ||
end | ||
|
||
def close | ||
@reader_env.close if @reader_env | ||
@writer_env.close if @writer_env | ||
end | ||
|
||
protected | ||
|
||
# Shorten key using hash digest to fit within LMDB's key-length limit. | ||
def digest(key) | ||
Digest::SHA2.hexdigest(key.to_s) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,3 +11,4 @@ milestone-cache_v2.json | |
/.sass-cache | ||
/cloudfront_aliases*.json | ||
mysql-status-cache.json | ||
i18n/cache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
require_relative 'test_helper' | ||
require 'cdo/lmdb_key_value' | ||
require 'active_support/core_ext/numeric/bytes' | ||
require 'tmpdir' | ||
|
||
class LMDBKeyValueTest < Minitest::Test | ||
def test_lmdb_key_value | ||
Dir.mktmpdir do |dir| | ||
lmdb = Cdo::LMDBKeyValue.new(dir, size: 100.kilobytes) | ||
assert_nil lmdb['key'] | ||
lmdb['key'] = 'value' | ||
assert_equal 'value', lmdb['key'] | ||
lmdb.close | ||
end | ||
end | ||
end |