rails/activesupport/test
Jonathan Hefner a2a6331451 Add ActiveSupport::MessagePack
`ActiveSupport::MessagePack` is a serializer that integrates with the
`msgpack` gem to serialize a variety of Ruby objects.  `AS::MessagePack`
supports several types beyond the base types that `msgpack` supports,
including `Time` and `Range`, as well as Active Support types such as
`AS::TimeWithZone` and `AS::HashWithIndifferentAccess`.

Compared to `JSON` and `Marshal`, `AS::MessagePack` can provide a
performance improvement and message size reduction.  For example, when
used with `MessageVerifier`:

  ```ruby
  # frozen_string_literal: true

  require "benchmark/ips"
  require "active_support/all"
  require "active_support/message_pack"

  marshal_verifier = ActiveSupport::MessageVerifier.new("secret", serializer: Marshal)
  json_verifier = ActiveSupport::MessageVerifier.new("secret", serializer: JSON)
  asjson_verifier = ActiveSupport::MessageVerifier.new("secret", serializer: ActiveSupport::JSON)
  msgpack_verifier = ActiveSupport::MessageVerifier.new("secret", serializer: ActiveSupport::MessagePack)

  ActiveSupport::Messages::Metadata.use_message_serializer_for_metadata = true
  expiry = 1.year.from_now
  data = { bool: true, num: 123456789, string: "x" * 50 }

  Benchmark.ips do |x|
    x.report("Marshal") do
      marshal_verifier.verify(marshal_verifier.generate(data, expires_at: expiry))
    end

    x.report("JSON") do
      json_verifier.verify(json_verifier.generate(data, expires_at: expiry))
    end

    x.report("AS::JSON") do
      asjson_verifier.verify(asjson_verifier.generate(data, expires_at: expiry))
    end

    x.report("MessagePack") do
      msgpack_verifier.verify(msgpack_verifier.generate(data, expires_at: expiry))
    end

    x.compare!
  end

  puts "Marshal size: #{marshal_verifier.generate(data, expires_at: expiry).bytesize}"
  puts "JSON size: #{json_verifier.generate(data, expires_at: expiry).bytesize}"
  puts "MessagePack size: #{msgpack_verifier.generate(data, expires_at: expiry).bytesize}"
  ```

  ```
  Warming up --------------------------------------
               Marshal     1.206k i/100ms
                  JSON     1.165k i/100ms
              AS::JSON   790.000  i/100ms
           MessagePack     1.798k i/100ms
  Calculating -------------------------------------
               Marshal     11.748k (± 1.3%) i/s -     59.094k in   5.031071s
                  JSON     11.498k (± 1.4%) i/s -     58.250k in   5.066957s
              AS::JSON      7.867k (± 2.4%) i/s -     39.500k in   5.024055s
           MessagePack     17.865k (± 0.8%) i/s -     89.900k in   5.032592s

  Comparison:
           MessagePack:    17864.9 i/s
               Marshal:    11747.8 i/s - 1.52x  (± 0.00) slower
                  JSON:    11498.4 i/s - 1.55x  (± 0.00) slower
              AS::JSON:     7866.9 i/s - 2.27x  (± 0.00) slower

  Marshal size: 254
  JSON size: 234
  MessagePack size: 194
  ```

Additionally, `ActiveSupport::MessagePack::CacheSerializer` is a
serializer that is suitable for use as an `ActiveSupport::Cache` coder.
`AS::MessagePack::CacheSerializer` can serialize `ActiveRecord::Base`
instances, including loaded associations.  Like `AS::MessagePack`, it
provides a performance improvement and payload size reduction:

  ```ruby
  # frozen_string_literal: true

  require "benchmark/ips"
  require "active_support/message_pack"

  ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")

  ActiveRecord::Schema.define do
    create_table :posts, force: true do |t|
      t.string :body
      t.timestamps
    end

    create_table :comments, force: true do |t|
      t.integer :post_id
      t.string :body
      t.timestamps
    end
  end

  class Post < ActiveRecord::Base
    has_many :comments
  end

  class Comment < ActiveRecord::Base
    belongs_to :post
  end

  post = Post.create!(body: "x" * 100)
  2.times { post.comments.create!(body: "x" * 100) }
  post.comments.load
  cache_entry = ActiveSupport::Cache::Entry.new(post)

  Rails70Coder = ActiveSupport::Cache::Coders::Rails70Coder
  CacheSerializer = ActiveSupport::MessagePack::CacheSerializer

  Benchmark.ips do |x|
    x.report("Rails70Coder") do
      Rails70Coder.load(Rails70Coder.dump(cache_entry))
    end

    x.report("CacheSerializer") do
      CacheSerializer.load(CacheSerializer.dump(cache_entry))
    end

    x.compare!
  end

  puts "Rails70Coder size: #{Rails70Coder.dump(cache_entry).bytesize}"
  puts "CacheSerializer size: #{CacheSerializer.dump(cache_entry).bytesize}"
  ```

  ```
  Warming up --------------------------------------
          Rails70Coder   329.000  i/100ms
       CacheSerializer   492.000  i/100ms
  Calculating -------------------------------------
          Rails70Coder      3.285k (± 1.7%) i/s -     16.450k in   5.008447s
       CacheSerializer      4.895k (± 2.4%) i/s -     24.600k in   5.028803s

  Comparison:
       CacheSerializer:     4894.7 i/s
          Rails70Coder:     3285.4 i/s - 1.49x  slower

  Rails70Coder size: 808
  CacheSerializer size: 593
  ```

Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
2023-04-17 11:56:06 -05:00
..
autoloading_fixtures Delete orphan autoloading fixtures 2021-04-06 20:14:10 +02:00
cache Cache: warning if `expires_in` is given an incorrect value 2023-04-09 09:57:47 -06:00
concurrency Make AbstractAdapter#lock thread local by default 2022-11-23 14:34:40 +01:00
core_ext Fix delegation to a method with implicit block. 2023-04-10 19:28:26 +02:00
dependencies Deletes AS::Dependencies::Blamable 2021-03-08 17:10:42 +01:00
deprecation Deprecate ActiveSupport::Deprecation instance delegation 2023-03-14 17:32:52 +01:00
file_fixtures introduce `ActiveSupport::Testing::FileFixtures`. 2015-01-28 12:29:34 +01:00
fixtures Concerns learn to be prepended 2020-02-10 02:23:33 +01:00
json Fixes https://github.com/rails/rails/issues/47267 2023-02-06 15:13:47 +00:00
message_pack Add ActiveSupport::MessagePack 2023-04-17 11:56:06 -05:00
messages Add ActiveSupport::MessagePack 2023-04-17 11:56:06 -05:00
notifications Only wrap subscriber exceptions if there is more than one 2021-11-02 16:47:37 +01:00
testing Remove the multi-call form of assert_called_with 2022-06-16 11:13:57 -04:00
xml_mini Remove leading whitespace from the XML under test 2021-01-05 22:29:25 -05:00
abstract_unit.rb Add ActiveSupport.deprecator 2022-10-25 15:06:39 -05:00
actionable_error_test.rb allow running each test with pure ruby path/to/test.rb 2019-12-18 08:49:19 -06:00
array_inquirer_test.rb Enable Lint/DuplicateMethods rubocop rule 2021-11-15 13:51:28 -05:00
autoload_test.rb allow running each test with pure ruby path/to/test.rb 2019-12-18 08:49:19 -06:00
benchmarkable_test.rb allow running each test with pure ruby path/to/test.rb 2019-12-18 08:49:19 -06:00
broadcast_logger_test.rb Fix test where broadcaster doesn't define #silence 2022-10-23 01:46:40 -04:00
callback_inheritance_test.rb Replace ableist language 2021-10-05 22:27:09 -04:00
callbacks_test.rb Add ability to run only before/around/after callbacks in run_callbacks 2022-09-08 14:41:54 +00:00
clean_backtrace_test.rb chore: fix grammar and spelling 2021-04-12 05:30:44 +10:00
clean_logger_test.rb Fix logger format with Ruby 3.1 2021-12-20 22:36:33 +00:00
concern_test.rb Fix variable not initialized warnings 2020-03-13 00:14:45 +01:00
configurable_test.rb ActiveSupport::Configurable default value option 2021-07-20 10:36:58 -03:00
configuration_file_test.rb Change `yaml` to `YAML` 2022-03-29 15:19:22 +10:00
constantize_test_cases.rb Remove DependenciesTestHelpers 2021-04-03 19:24:12 +02:00
constantize_test_helpers.rb Remove DependenciesTestHelpers 2021-04-03 19:24:12 +02:00
current_attributes_test.rb `ActiveSupport::CurrentAttributes`: raise if a restricted attribute name is used. 2023-01-15 17:07:11 -07:00
dependencies_test.rb Delete the classic version of AS::Dependencies.clear 2021-08-23 10:05:08 +02:00
deprecation_test.rb Use the singleton instance as ActiveSupport deprecator 2023-03-14 17:33:48 +01:00
descendants_tracker_test.rb Filter reloaded classes in Class#subclasses and Class#descendants core exts 2022-09-28 12:08:58 +02:00
digest_test.rb Change the default digest for new apps to SHA256 2021-01-08 12:07:20 +01:00
encrypted_configuration_test.rb Save encrypted config even if YAML is invalid 2022-07-28 16:37:18 -05:00
encrypted_file_test.rb Hard code serializer for AS::EncryptedFile 2023-01-22 16:26:47 -06:00
environment_inquirer_test.rb Add Rails.env.local? (#46786) 2022-12-21 15:38:33 +01:00
error_reporter_test.rb Allow ErrorReporter to handle several error classes 2022-10-21 13:21:36 +02:00
evented_file_update_checker_test.rb Improve reliability of EventedFileUpdateCheckerTest fork test 2023-03-24 14:24:07 +01:00
execution_context_test.rb Extract ActiveSupport::ExecutionContext out of ActiveRecord::QueryLogs 2021-11-10 09:36:02 +01:00
executor_test.rb RubyVM class serial is no longer available in Ruby 3.2 2022-12-15 15:45:27 +09:00
file_update_checker_shared_tests.rb Avoid double wait in EventedFileUpdateCheckerTest 2021-07-22 12:29:27 -05:00
file_update_checker_test.rb Avoid double wait in EventedFileUpdateCheckerTest 2021-07-22 12:29:27 -05:00
fork_tracker_test.rb Make ForkTracker.check! a noop on Ruby 3.1+ 2023-02-16 17:32:38 +01:00
gzip_test.rb allow running each test with pure ruby path/to/test.rb 2019-12-18 08:49:19 -06:00
hash_with_indifferent_access_test.rb Let HWIA#transform_keys take a Hash argument like Hash#transform_keys 2023-01-11 12:32:06 +09:00
i18n_test.rb allow running each test with pure ruby path/to/test.rb 2019-12-18 08:49:19 -06:00
inflector_test.rb Ensure `Inflector` methods return un-frozen strings 2023-03-28 08:50:54 -06:00
inflector_test_cases.rb Fix (Inflector::Methods#underscore): small regression 2021-10-28 00:25:09 +02:00
isolated_execution_state_test.rb Introduce ActiveSupport::IsolatedExecutionState for internal use 2021-11-18 15:55:15 +01:00
key_generator_test.rb ✂️ [ci skip] 2021-03-22 04:46:11 +09:00
lazy_load_hooks_test.rb allow running each test with pure ruby path/to/test.rb 2019-12-18 08:49:19 -06:00
log_subscriber_test.rb Never report negative idle time 2023-01-29 04:56:50 +10:30
logger_test.rb LoggerThreadSafeLevel only impact the receiving logger 2022-11-24 12:39:52 +01:00
message_encryptor_test.rb Use throw for message error handling control flow 2023-02-12 15:16:25 -06:00
message_encryptors_test.rb Use throw for message error handling control flow 2023-02-12 15:16:25 -06:00
message_verifier_test.rb Use throw for message error handling control flow 2023-02-12 15:16:25 -06:00
message_verifiers_test.rb Support Message{Encryptors,Verifiers}#rotate block 2022-12-19 16:35:20 -06:00
multibyte_chars_test.rb Rename behaviour to behavior in test case names 2022-05-26 17:14:18 -04:00
multibyte_proxy_test.rb allow running each test with pure ruby path/to/test.rb 2019-12-18 08:49:19 -06:00
multibyte_test_helpers.rb Remove deprecated methods in ActiveSupport::Multibyte::Unicode 2020-10-30 00:26:02 +00:00
notifications_test.rb Never report negative idle time 2023-01-29 04:56:50 +10:30
number_helper_i18n_test.rb Add `:round_mode` parameter support to number helpers 2020-01-05 21:23:02 +00:00
number_helper_test.rb add zetta to number_to_human_size 2023-03-28 19:50:13 +09:00
option_merger_test.rb Add test coverage for `Object#with_options` with `Hash`-like 2023-02-13 22:08:23 -05:00
ordered_hash_test.rb allow running each test with pure ruby path/to/test.rb 2019-12-18 08:49:19 -06:00
ordered_options_test.rb Removed unwanted requires of and fixed rubocop errors 2022-03-10 01:19:55 +05:30
parameter_filter_test.rb Add AS::ParameterFilter.precompile_filters 2022-11-24 10:26:54 -06:00
reloader_test.rb ActiveSupport::Reloader should not report exception 2022-09-22 17:46:18 +02:00
rescuable_test.rb allow running each test with pure ruby path/to/test.rb 2019-12-18 08:49:19 -06:00
rotation_coordinator_tests.rb Support Message{Encryptors,Verifiers}#rotate block 2022-12-19 16:35:20 -06:00
safe_buffer_test.rb [Fix #47343] maintain html_safe? on sliced HTML safe strings 2023-02-09 16:51:22 -04:00
secure_compare_rotator_test.rb allow running each test with pure ruby path/to/test.rb 2019-12-18 08:49:19 -06:00
security_utils_test.rb secure_compare: Check byte size instead of length 2021-04-02 12:45:29 -04:00
share_lock_test.rb ShareLock#exclusive and sharing are both public methods 2020-10-02 15:53:47 +09:00
silence_logger_test.rb allow running each test with pure ruby path/to/test.rb 2019-12-18 08:49:19 -06:00
string_inquirer_test.rb Enable Lint/DuplicateMethods rubocop rule 2021-11-15 13:51:28 -05:00
subscriber_test.rb Rename behaviour to behavior in test case names 2022-05-26 17:14:18 -04:00
tagged_logging_test.rb Test TaggedLogging#tagged with single array arg 2023-01-14 12:06:39 -06:00
test_case_test.rb Include amount changed by in `assert_difference` failure message 2023-01-06 12:25:34 -07:00
time_travel_test.rb Stub `Time.new()` in `TimeHelpers#travel_to` 2023-02-14 11:28:52 +02:00
time_zone_test.rb Update ActiveSupport time zone tests for UTC-12 (International Date Line West) 2022-04-11 09:35:41 -05:00
time_zone_test_helpers.rb Add ActiveSupport.deprecator 2022-10-25 15:06:39 -05:00
transliterate_test.rb Return a copy of the source string when transliterating 2022-11-30 16:55:46 +00:00
xml_mini_test.rb Allow to opt-in to the new TimeWithZone.name and fix XmlMini serialization 2021-04-12 22:03:31 +02:00