New Game : Bubble'z popper

PR Description 📜
I added a simple bubble-popping game using HTML, CSS, and Javascript.
By moving the cursor onto the bubbles they blast. If you miss the 10 chances the game will end.
Fixes #4212

Mark the task you have completed ✅

[x] I follow CONTRIBUTING GUIDELINE & CODE OF CONDUCT of this project.
[x] I have performed a self-review of my own code or work.
[x] I have commented my code, particularly in hard-to-understand areas.
[x] My changes generates no new warnings.
[x] I have followed proper naming convention showed in CONTRIBUTING GUIDELINE
[x] I have added screenshot for website preview in assets/images
[x] I have added entries for my game in main README.md
[x] I have added README.md in my folder
[x] I have added working video of the game in README.md (optional)
[x] I have specified the respective issue number for which I have requested the new game.

Add your screenshots(Optional) 📸

https://github.com/kunjgit/GameZone/assets/125206920/64271834-48f4-41fb-ba59-dadf44ea453e

Thank you soo much for contributing to our repository 💗

Thank you @Chandu6702 ,for creating the PR and contributing to our GameZone 💗
Review team will review the PR and will reach out to you soon! 😇
Make sure that you have marked all the tasks that you are done with ✅.
Thank you for your patience! 😀

@kunjgit  please review and merge this PR

@kunjgit please review and merge this PR

@kunjgit please review and merge this PR

@kunjgit please review and merge this PR

@kunjgit please review and merge this PR

@kunjgit please review and merge this PR

@ishita-43 please review this PR

@kunjgit please review and merge this pr

@ishita-43 please review this PR

@ishita-43 and @kunjgit please review this PR
It's been 3 days since I created this PR,
Other PRs which are created later have been merged
I don't know what's the problem can you please review this PR and let me know if I need to change anything.

@ishita-43 I have resolved the issues and updated the README.md file,please review this PR

@Chandu6702 resolve merge conflicts

@kunjgit I resolved the conflicts please review and merge this PR

Thank you  @Chandu6702 , for your valuable time and contribution in our GameZone 💗.
It’s our GameZone, so Let’s build this GameZone altogether !!🤝
Hoping to see you soon with another PR again 😇
Wishing you all the best for your journey into Open Source🚀


Add TabWidthMode Property to TabStrip/TabControl

Is your feature request related to a problem? Please describe.
In some scenarios its common to automatically size tab item header widths to (1) fit available width and (2) keep uniform width among each tab item.
In WPF this was never supported directly; however, UWP TabView has a property:
TabView.TabWidthMode
TabViewWidthMode

This should be added to TabStrip and TabControl
Describe the solution you'd like
A clear and concise description of what you want to happen.
Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
Additional context
Add any other context or screenshots about the feature request here.

I see possible implementation using custom tabs Panel type that arranges children in a way depending on this property.

(1) fit available width and (2) keep uniform width among each tab item.

You can set TabControl.ItemPanel to UniformGrid with specific number of columns. Which isn't the same but can solve it mostly. Especially with converter from items count to a rows count, when you want to limit columns as well.
Together with other types of panels, including custom ones, TabWidthMode would be limiting.
Saying that, I personally don't see problems of having both TabWidthMode for simple well-known scenarios and ItemPanel for more advanced.

You can set TabControl.ItemPanel to UniformGrid with specific number of columns.

Yes, this is what I'm doing now. I just realized TabView had this built in and it's pretty nice to have available.

Saying that, I personally don't see problems of having both TabWidthMode for simple well-known scenarios and ItemPanel for more advanced.

Yes, I agree ItemPanel should still be supported.


chore(deps): update dependency rails to v7

This PR contains the following updates:

Package
Change
Age
Adoption
Passing
Confidence

rails (source, changelog)
'~> 6.1.7' -> '~> 7.1.0'

Release Notes

rails/rails (rails)
v7.1.1: 7.1.1
Compare Source
Active Support

Add support for keyword arguments when delegating calls to custom loggers from ActiveSupport::BroadcastLogger.
Jenny Shen

NumberHelper: handle objects responding to_d.
fatkodima

Fix RedisCacheStore to properly set the TTL when incrementing or decrementing.
This bug was only impacting Redis server older than 7.0.
Thomas Countz

Fix MemoryStore to prevent race conditions when incrementing or decrementing.
Pierre Jambet

Active Model

No changes.

Active Record

Fix auto populating IDENTITY columns for PostgreSQL.
fatkodima

Fix "ArgumentError: wrong number of arguments (given 3, expected 2)" when
down migrating rename_table in older migrations.
fatkodima

Do not require the Action Text, Active Storage and Action Mailbox tables
to be present when running when running test on CI.
Rafael Mendonça França

Action View

Updated @rails/ujs files to ignore certain data-* attributes when element is contenteditable.
This fix was already landed in >= <IP_ADDRESS>, < 7.1.0.
[CVE-2023-23913]
Ryunosuke Sato

Action Pack

No changes.

Active Job

Don't log enqueuing details when the job wasn't enqueued.
Dustin Brown

Action Mailer

No changes.

Action Cable

No changes.

Active Storage

No changes.

Action Mailbox

No changes.

Action Text

No changes.

Railties

Ensures the Rails generated Dockerfile uses correct ruby version and matches Gemfile.
Abhay Nikam

v7.1.0: 7.1.0
Compare Source
Active Support

Fix AS::MessagePack with ENV["RAILS_MAX_THREADS"].
Jonathan Hefner

Add a new public API for broadcasting logs
This feature existed for a while but was until now a private API.
Broadcasting log allows to send log message to difference sinks (STDOUT, a file ...) and
is used by default in the development environment to write logs both on STDOUT and in the
"development.log" file.
Basic usage:
stdout_logger = Logger.new(STDOUT)
file_logger = Logger.new("development.log")
broadcast = ActiveSupport::BroadcastLogger.new(stdout_logger, file_logger)

broadcast.info("Hello!") # The "Hello!" message is written on STDOUT and in the log file.

Adding other sink(s) to the broadcast:
broadcast = ActiveSupport::BroadcastLogger.new
broadcast.broadcast_to(Logger.new(STDERR))

Remove a sink from the broadcast:
stdout_logger = Logger.new(STDOUT)
broadcast = ActiveSupport::BroadcastLogger.new(stdout_logger)

broadcast.stop_broadcasting_to(stdout_logger)

Edouard Chin

Fix Range#overlap? not taking empty ranges into account on Ruby < 3.3
Nobuyoshi Nakada, Shouichi Kamiya, Hartley McGuire

Use Ruby 3.3 Range#overlap? if available
Yasuo Honda

Add bigdecimal as Active Support dependency that is a bundled gem candidate for Ruby 3.4.
bigdecimal 3.1.4 or higher version will be installed.
Ruby 2.7 and 3.0 users who want bigdecimal version 2.0.0 or 3.0.0 behavior as a default gem,
pin the bigdecimal version in your application Gemfile.
Koichi ITO

Add drb, mutex_m and base64 that are bundled gem candidates for Ruby 3.4
Yasuo Honda

When using cache format version >= 7.1 or a custom serializer, expired and
version-mismatched cache entries can now be detected without deserializing
their values.
Jonathan Hefner

Make all cache stores return a boolean for #delete
Previously the RedisCacheStore#delete would return 1 if the entry
exists and 0 otherwise. Now it returns true if the entry exists and false
otherwise, just like the other stores.
The FileStore would return nil if the entry doesn't exists and returns
false now as well.
Petrik de Heus

Active Support cache stores now support replacing the default compressor via
a :compressor option. The specified compressor must respond to deflate
and inflate. For example:
module MyCompressor
  def self.deflate(string)

compression logic...
    end

    def self.inflate(compressed)

decompression logic...
    end
  end

  config.cache_store = :redis_cache_store, { compressor: MyCompressor }
  ```

*Jonathan Hefner*

Active Support cache stores now support a :serializer option. Similar to
the :coder option, serializers must respond to dump and load. However,
serializers are only responsible for serializing a cached value, whereas
coders are responsible for serializing the entire ActiveSupport::Cache::Entry
instance.  Additionally, the output from serializers can be automatically
compressed, whereas coders are responsible for their own compression.
Specifying a serializer instead of a coder also enables performance
optimizations, including the bare string optimization introduced by cache
format version 7.1.
The :serializer and :coder options are mutually exclusive. Specifying
both will raise an ArgumentError.
Jonathan Hefner

Fix ActiveSupport::Inflector.humanize(nil) raising NoMethodError: undefined method `end_with?' for nil:NilClass.
James Robinson

Don't show secrets for ActiveSupport::KeyGenerator#inspect.
Before:
ActiveSupport::KeyGenerator.new(secret).inspect
"#<ActiveSupport::KeyGenerator:0x0000000104888038 ... @secret=\"\\xAF\\bFh]LV}q\\nl\\xB2U\\xB3 ... >"

After:
ActiveSupport::KeyGenerator::Aes256Gcm(secret).inspect
"#<ActiveSupport::KeyGenerator:0x0000000104888038>"

Petrik de Heus

Improve error message when EventedFileUpdateChecker is used without a
compatible version of the Listen gem
Hartley McGuire

Add :report behavior for Deprecation
Setting config.active_support.deprecation = :report uses the error
reporter to report deprecation warnings to ActiveSupport::ErrorReporter.
Deprecations are reported as handled errors, with a severity of :warning.
Useful to report deprecations happening in production to your bug tracker.
Étienne Barrié

Rename Range#overlaps? to #overlap? and add alias for backwards compatibility
Christian Schmidt

Fix EncryptedConfiguration returning incorrect values for some Hash
methods
Hartley McGuire

Don't show secrets for MessageEncryptor#inspect.
Before:
ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm").inspect
"#<ActiveSupport::MessageEncryptor:0x0000000104888038 ... @secret=\"\\xAF\\bFh]LV}q\\nl\\xB2U\\xB3 ... >"

After:
ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm").inspect
"#<ActiveSupport::MessageEncryptor:0x0000000104888038>"

Petrik de Heus

Don't show contents for EncryptedConfiguration#inspect.
Before:
Rails.application.credentials.inspect
"#<ActiveSupport::EncryptedConfiguration:0x000000010d2b38e8 ... @config={:secret=>\"something secret\"} ... @key_file_contents=\"915e4ea054e011022398dc242\" ...>"

After:
Rails.application.credentials.inspect
"#<ActiveSupport::EncryptedConfiguration:0x000000010d2b38e8>"

Petrik de Heus

ERB::Util.html_escape_once always returns an html_safe string.
This method previously maintained the html_safe? property of a string on the return
value. Because this string has been escaped, however, not marking it as html_safe causes
entities to be double-escaped.
As an example, take this view snippet:
<p><%= html_escape_once("this & that & the other") %></p>

Before this change, that would be double-escaped and render as:
<p>this &amp; that &amp; the other</p>

After this change, it renders correctly as:
<p>this & that & the other</p>

Fixes #48256
Mike Dalessio

Deprecate SafeBuffer#clone_empty.
This method has not been used internally since Rails 4.2.0.
Mike Dalessio

MessageEncryptor, MessageVerifier, and config.active_support.message_serializer
now accept :message_pack and :message_pack_allow_marshal as serializers.
These serializers require the msgpack gem
(>= 1.7.0).
The Message Pack format can provide improved performance and smaller payload
sizes. It also supports round-tripping some Ruby types that are not supported
by JSON. For example:
verifier = ActiveSupport::MessageVerifier.new("secret")
data = [{ a: 1 }, { b: 2 }.with_indifferent_access, 1.to_d, Time.at(0, 123)]
message = verifier.generate(data)

BEFORE with config.active_support.message_serializer = :json
  verifier.verified(message)

=> [{"a"=>1}, {"b"=>2}, "1.0", "1969-12-31T18:00:00.000-06:00"]
  verifier.verified(message).map(&:class)

=> [Hash, Hash, String, String]
AFTER with config.active_support.message_serializer = :message_pack
  verifier.verified(message)

=> [{:a=>1}, {"b"=>2}, 0.1e1, 1969-12-31 18:00:00.000123 -0600]
  verifier.verified(message).map(&:class)

=> [Hash, ActiveSupport::HashWithIndifferentAccess, BigDecimal, Time]
  ```

The `:message_pack` serializer can fall back to deserializing with
`ActiveSupport::JSON` when necessary, and the `:message_pack_allow_marshal`
serializer can fall back to deserializing with `Marshal` as well as
`ActiveSupport::JSON`. Additionally, the `:marshal`, `:json`, and
`:json_allow_marshal` serializers can now fall back to deserializing with
`ActiveSupport::MessagePack` when necessary. These behaviors ensure old
messages can still be read so that migration is easier.

*Jonathan Hefner*

A new 7.1 cache format is available which includes an optimization for
bare string values such as view fragments.
The 7.1 cache format is used by default for new apps, and existing apps
can enable the format by setting config.load_defaults 7.1 or by setting
config.active_support.cache_format_version = 7.1 in config/application.rb
or a config/environments/*.rb file.
Cache entries written using the 6.1 or 7.0 cache formats can be read
when using the 7.1 format. To perform a rolling deploy of a Rails 7.1
upgrade, wherein servers that have not yet been upgraded must be able to
read caches from upgraded servers, leave the cache format unchanged on the
first deploy, then enable the 7.1 cache format on a subsequent deploy.
Jonathan Hefner

Active Support cache stores can now use a preconfigured serializer based on
ActiveSupport::MessagePack via the :serializer option:
config.cache_store = :redis_cache_store, { serializer: :message_pack }

The :message_pack serializer can reduce cache entry sizes and improve
performance, but requires the msgpack gem
(>= 1.7.0).
The :message_pack serializer can read cache entries written by the default
serializer, and the default serializer can now read entries written by the
:message_pack serializer. These behaviors make it easy to migrate between
serializer without invalidating the entire cache.
Jonathan Hefner

Object#deep_dup no longer duplicate named classes and modules.
Before:
hash = { class: Object, module: Kernel }
hash.deep_dup # => {:class=>#<Class:0x00000001063ffc80>, :module=>#<Module:0x00000001063ffa00>}

After:
hash = { class: Object, module: Kernel }
hash.deep_dup # => {:class=>Object, :module=>Kernel}

Jean Boussier

Consistently raise an ArgumentError if the ActiveSupport::Cache key is blank.
Joshua Young

Deprecate usage of the singleton ActiveSupport::Deprecation.
All usage of ActiveSupport::Deprecation as a singleton is deprecated, the most common one being
ActiveSupport::Deprecation.warn. Gem authors should now create their own deprecator (ActiveSupport::Deprecation
object), and use it to emit deprecation warnings.
Calling any of the following without specifying a deprecator argument is also deprecated:

Module.deprecate
deprecate_constant
DeprecatedObjectProxy
DeprecatedInstanceVariableProxy
DeprecatedConstantProxy
deprecation-related test assertions

Use of ActiveSupport::Deprecation.silence and configuration methods like behavior=, disallowed_behavior=,
disallowed_warnings= should now be aimed at the application's deprecators.
Rails.application.deprecators.silence do

code that emits deprecation warnings
end
```

If your gem has a Railtie or Engine, it's encouraged to add your deprecator to the application's deprecators, that
way the deprecation related configuration options will apply to it as well, e.g.
`config.active_support.report_deprecations` set to `false` in the production environment will also disable your
deprecator.

```ruby
initializer "my_gem.deprecator" do |app|
  app.deprecators[:my_gem] = MyGem.deprecator
end
```

*Étienne Barrié*

Add Object#with to set and restore public attributes around a block
client.timeout # => 5
client.with(timeout: 1) do
  client.timeout # => 1
end
client.timeout # => 5

Jean Boussier

Remove deprecated support to generate incorrect RFC 4122 UUIDs when providing a namespace ID that is not one of the
constants defined on Digest::UUID.
Rafael Mendonça França

Deprecate config.active_support.use_rfc4122_namespaced_uuids.
Rafael Mendonça França

Remove implicit conversion of objects into String by ActiveSupport::SafeBuffer.
Rafael Mendonça França

Remove deprecated active_support/core_ext/range/include_time_with_zone file.
Rafael Mendonça França

Deprecate config.active_support.remove_deprecated_time_with_zone_name.
Rafael Mendonça França

Remove deprecated override of ActiveSupport::TimeWithZone.name.
Rafael Mendonça França

Deprecate config.active_support.disable_to_s_conversion.
Rafael Mendonça França

Remove deprecated option to passing a format to #to_s in Array, Range, Date, DateTime, Time,
BigDecimal, Float and, Integer.
Rafael Mendonça França

Remove deprecated ActiveSupport::PerThreadRegistry.
Rafael Mendonça França

Remove deprecated override of Enumerable#sum.
Rafael Mendonça França

Deprecated initializing a ActiveSupport::Cache::MemCacheStore with an instance of Dalli::Client.
Deprecate the undocumented option of providing an already-initialized instance of Dalli::Client to ActiveSupport::Cache::MemCacheStore. Such clients could be configured with unrecognized options, which could lead to unexpected behavior. Instead, provide addresses as documented.
aledustet

Stub Time.new() in TimeHelpers#travel_to
travel_to Time.new(2004, 11, 24) do

Inside the travel_to block Time.new is stubbed
    assert_equal 2004, Time.new.year
  end
  ```

*fatkodima*

Raise ActiveSupport::MessageEncryptor::InvalidMessage from
ActiveSupport::MessageEncryptor#decrypt_and_verify regardless of cipher.
Previously, when a MessageEncryptor was using a non-AEAD cipher such as
AES-256-CBC, a corrupt or tampered message would raise
ActiveSupport::MessageVerifier::InvalidSignature.  Now, all ciphers raise
the same error:
encryptor = ActiveSupport::MessageEncryptor.new("x" * 32, cipher: "aes-256-gcm")
message = encryptor.encrypt_and_sign("message")
encryptor.decrypt_and_verify(message.next)

=> raises ActiveSupport::MessageEncryptor::InvalidMessage
  encryptor = ActiveSupport::MessageEncryptor.new("x" * 32, cipher: "aes-256-cbc")
  message = encryptor.encrypt_and_sign("message")
  encryptor.decrypt_and_verify(message.next)

BEFORE:
=> raises ActiveSupport::MessageVerifier::InvalidSignature
AFTER:
=> raises ActiveSupport::MessageEncryptor::InvalidMessage
  ```

*Jonathan Hefner*

Support nil original values when using ActiveSupport::MessageVerifier#verify.
Previously, MessageVerifier#verify did not work with nil original
values, though both MessageVerifier#verified and
MessageEncryptor#decrypt_and_verify do:
encryptor = ActiveSupport::MessageEncryptor.new(secret)
message = encryptor.encrypt_and_sign(nil)

encryptor.decrypt_and_verify(message)

=> nil
  verifier = ActiveSupport::MessageVerifier.new(secret)
  message = verifier.generate(nil)

  verifier.verified(message)

=> nil
  verifier.verify(message)

BEFORE:
=> raises ActiveSupport::MessageVerifier::InvalidSignature
AFTER:
=> nil
  ```

*Jonathan Hefner*

Maintain html_safe? on html_safe strings when sliced with slice, slice!, or chr method.
Previously, html_safe? was only maintained when the html_safe strings were sliced
with [] method. Now, slice, slice!, and chr methods will maintain html_safe? like [] method.
string = "
test
".html_safe
string.slice(0, 1).html_safe? # => true
string.slice!(0, 1).html_safe? # => true

maintain html_safe? after the slice!
string.html_safe? # => true
string.chr.html_safe? # => true
```

*Michael Go*

Add Object#in? support for open ranges.
assert Date.today.in?(..Date.tomorrow)
assert_not Date.today.in?(Date.tomorrow..)

Ignacio Galindo

config.i18n.raise_on_missing_translations = true now raises on any missing translation.
Previously it would only raise when called in a view or controller. Now it will raise
anytime I18n.t is provided an unrecognised key.
If you do not want this behaviour, you can customise the i18n exception handler. See the
upgrading guide or i18n guide for more information.
Alex Ghiculescu

ActiveSupport::CurrentAttributes now raises if a restricted attribute name is used.
Attributes such as set and reset cannot be used as they clash with the
CurrentAttributes public API.
Alex Ghiculescu

HashWithIndifferentAccess#transform_keys now takes a Hash argument, just
as Ruby's Hash#transform_keys does.
Akira Matsuda

delegate now defines method with proper arity when delegating to a Class.
With this change, it defines faster method (3.5x faster with no argument).
However, in order to gain this benefit, the delegation target method has to
be defined before declaring the delegation.

This defines 3.5 times faster method than before
class C
  def self.x() end
  delegate :x, to: :class
end

class C

This works but silently falls back to old behavior because
delegate cannot find the definition of x
  delegate :x, to: :class
  def self.x() end
end
```

*Akira Matsuda*

assert_difference message now includes what changed.
This makes it easier to debug non-obvious failures.
Before:
"User.count" didn't change by 32.
Expected: 1611
  Actual: 1579

After:
"User.count" didn't change by 32, but by 0.
Expected: 1611
  Actual: 1579

Alex Ghiculescu

Add ability to match exception messages to assert_raises assertion
Instead of this
error = assert_raises(ArgumentError) do
  perform_service(param: 'exception')
end
assert_match(/incorrect param/i, error.message)

you can now write this
assert_raises(ArgumentError, match: /incorrect param/i) do
  perform_service(param: 'exception')
end

fatkodima

Add Rails.env.local? shorthand for Rails.env.development? || Rails.env.test?.
DHH

ActiveSupport::Testing::TimeHelpers now accepts named with_usec argument
to freeze_time, travel, and travel_to methods. Passing true prevents
truncating the destination time with change(usec: 0).
KevSlashNull, and serprex

ActiveSupport::CurrentAttributes.resets now accepts a method name
The block API is still the recommended approach, but now both APIs are supported:
class Current < ActiveSupport::CurrentAttributes
  resets { Time.zone = nil }
  resets :clear_time_zone
end

Alex Ghiculescu

Ensure ActiveSupport::Testing::Isolation::Forking closes pipes
Previously, Forking.run_in_isolation opened two ends of a pipe. The fork
process closed the read end, wrote to it, and then terminated (which
presumably closed the file descriptors on its end). The parent process
closed the write end, read from it, and returned, never closing the read
end.
This resulted in an accumulation of open file descriptors, which could
cause errors if the limit is reached.
Sam Bostock

Fix Time#change and Time#advance for times around the end of Daylight
Saving Time.
Previously, when Time#change or Time#advance constructed a time inside
the final stretch of Daylight Saving Time (DST), the non-DST offset would
always be chosen for local times:

DST ended just before 2021-11-07 2:00:00 AM in US/Eastern.
ENV["TZ"] = "US/Eastern"

time = Time.local(2021, 11, 07, 00, 59, 59) + 1

=> 2021-11-07 01:00:00 -0400
time.change(day: 07)

=> 2021-11-07 01:00:00 -0500
time.advance(seconds: 0)

=> 2021-11-07 01:00:00 -0500
time = Time.local(2021, 11, 06, 01, 00, 00)

=> 2021-11-06 01:00:00 -0400
time.change(day: 07)

=> 2021-11-07 01:00:00 -0500
time.advance(days: 1)

=> 2021-11-07 01:00:00 -0500
```

And the DST offset would always be chosen for times with a `TimeZone`
object:

```ruby
Time.zone = "US/Eastern"

time = Time.new(2021, 11, 07, 02, 00, 00, Time.zone) - 3600

=> 2021-11-07 01:00:00 -0500
time.change(day: 07)

=> 2021-11-07 01:00:00 -0400
time.advance(seconds: 0)

=> 2021-11-07 01:00:00 -0400
time = Time.new(2021, 11, 8, 01, 00, 00, Time.zone)

=> 2021-11-08 01:00:00 -0500
time.change(day: 07)

=> 2021-11-07 01:00:00 -0400
time.advance(days: -1)

=> 2021-11-07 01:00:00 -0400
```

Now, `Time#change` and `Time#advance` will choose the offset that matches
the original time's offset when possible:

```ruby
ENV["TZ"] = "US/Eastern"

time = Time.local(2021, 11, 07, 00, 59, 59) + 1

=> 2021-11-07 01:00:00 -0400
time.change(day: 07)

=> 2021-11-07 01:00:00 -0400
time.advance(seconds: 0)

=> 2021-11-07 01:00:00 -0400
time = Time.local(2021, 11, 06, 01, 00, 00)

=> 2021-11-06 01:00:00 -0400
time.change(day: 07)

=> 2021-11-07 01:00:00 -0400
time.advance(days: 1)

=> 2021-11-07 01:00:00 -0400
Time.zone = "US/Eastern"

time = Time.new(2021, 11, 07, 02, 00, 00, Time.zone) - 3600

=> 2021-11-07 01:00:00 -0500
time.change(day: 07)

=> 2021-11-07 01:00:00 -0500
time.advance(seconds: 0)

=> 2021-11-07 01:00:00 -0500
time = Time.new(2021, 11, 8, 01, 00, 00, Time.zone)

=> 2021-11-08 01:00:00 -0500
time.change(day: 07)

=> 2021-11-07 01:00:00 -0500
time.advance(days: -1)

=> 2021-11-07 01:00:00 -0500
```

*Kevin Hall*, *Takayoshi Nishida*, and *Jonathan Hefner*

Fix MemoryStore to preserve entries TTL when incrementing or decrementing
This is to be more consistent with how MemCachedStore and RedisCacheStore behaves.
Jean Boussier

Rails.error.handle and Rails.error.record filter now by multiple error classes.
Rails.error.handle(IOError, ArgumentError) do
  1 + '1' # raises TypeError
end
1 + 1 # TypeErrors are not IOErrors or ArgumentError, so this will *not* be handled

Martin Spickermann

Class#subclasses and Class#descendants now automatically filter reloaded classes.
Previously they could return old implementations of reloadable classes that have been
dereferenced but not yet garbage collected.
They now automatically filter such classes like DescendantTracker#subclasses and
DescendantTracker#descendants.
Jean Boussier

Rails.error.report now marks errors as reported to avoid reporting them twice.
In some cases, users might want to report errors explicitly with some extra context
before letting it bubble up.
This also allows to safely catch and report errors outside of the execution context.
Jean Boussier

Add assert_error_reported and assert_no_error_reported
Allows to easily asserts an error happened but was handled
report = assert_error_reported(IOError) do

...
end
assert_equal "Oops", report.error.message
assert_equal "admin", report.context[:section]
assert_equal :warning, report.severity
assert_predicate report, :handled?
```

*Jean Boussier*

ActiveSupport::Deprecation behavior callbacks can now receive the
deprecator instance as an argument.  This makes it easier for such callbacks
to change their behavior based on the deprecator's state.  For example,
based on the deprecator's debug flag.
3-arity and splat-args callbacks such as the following will now be passed
the deprecator instance as their third argument:

->(message, callstack, deprecator) { ... }
->(*args) { ... }
->(message, *other_args) { ... }

2-arity and 4-arity callbacks such as the following will continue to behave
the same as before:

->(message, callstack) { ... }
->(message, callstack, deprecation_horizon, gem_name) { ... }
->(message, callstack, *deprecation_details) { ... }

Jonathan Hefner

ActiveSupport::Deprecation#disallowed_warnings now affects the instance on
which it is configured.
This means that individual ActiveSupport::Deprecation instances can be
configured with their own disallowed warnings, and the global
ActiveSupport::Deprecation.disallowed_warnings now only affects the global
ActiveSupport::Deprecation.warn.
Before
ActiveSupport::Deprecation.disallowed_warnings = ["foo"]
deprecator = ActiveSupport::Deprecation.new("2.0", "MyCoolGem")
deprecator.disallowed_warnings = ["bar"]

ActiveSupport::Deprecation.warn("foo") # => raise ActiveSupport::DeprecationException
ActiveSupport::Deprecation.warn("bar") # => print "DEPRECATION WARNING: bar"
deprecator.warn("foo")                 # => raise ActiveSupport::DeprecationException
deprecator.warn("bar")                 # => print "DEPRECATION WARNING: bar"

After
ActiveSupport::Deprecation.disallowed_warnings = ["foo"]
deprecator = ActiveSupport::Deprecation.new("2.0", "MyCoolGem")
deprecator.disallowed_warnings = ["bar"]

ActiveSupport::Deprecation.warn("foo") # => raise ActiveSupport::DeprecationException
ActiveSupport::Deprecation.warn("bar") # => print "DEPRECATION WARNING: bar"
deprecator.warn("foo")                 # => print "DEPRECATION WARNING: foo"
deprecator.warn("bar")                 # => raise ActiveSupport::DeprecationException

Note that global ActiveSupport::Deprecation methods such as ActiveSupport::Deprecation.warn
and ActiveSupport::Deprecation.disallowed_warnings have been deprecated.
Jonathan Hefner

Add italic and underline support to ActiveSupport::LogSubscriber#color
Previously, only bold text was supported via a positional argument.
This allows for bold, italic, and underline options to be specified
for colored logs.
info color("Hello world!", :red, bold: true, underline: true)

Gannon McGibbon

Add String#downcase_first method.
This method is the corollary of String#upcase_first.
Mark Schneider

thread_mattr_accessor will call .dup.freeze on non-frozen default values.
This provides a basic level of protection against different threads trying
to mutate a shared default object.
Jonathan Hefner

Add raise_on_invalid_cache_expiration_time config to ActiveSupport::Cache::Store
Specifies if an ArgumentError should be raised if Rails.cache fetch or
write are given an invalid expires_at or expires_in time.
Options are true, and false. If false, the exception will be reported
as handled and logged instead. Defaults to true if config.load_defaults >= 7.1.
Trevor Turk

ActiveSupport::Cache:Store#fetch now passes an options accessor to the block.
It makes possible to override cache options:
Rails.cache.fetch("3rd-party-token") do |name, options|
  token = fetch_token_from_remote

set cache's TTL to match token's TTL
      options.expires_in = token.expires_in
      token
    end

*Andrii Gladkyi*, *Jean Boussier*

default option of thread_mattr_accessor now applies through inheritance and
also across new threads.
Previously, the default value provided was set only at the moment of defining
the attribute writer, which would cause the attribute to be uninitialized in
descendants and in other threads.
Fixes #43312.
Thierry Deo

Redis cache store is now compatible with redis-rb 5.0.
Jean Boussier

Add skip_nil: support to ActiveSupport::Cache::Store#fetch_multi.
Daniel Alfaro

Add quarter method to date/time
Matt Swanson

Fix NoMethodError on custom ActiveSupport::Deprecation behavior.
ActiveSupport::Deprecation.behavior= was supposed to accept any object
that responds to call, but in fact its internal implementation assumed that
this object could respond to arity, so it was restricted to only Proc objects.
This change removes this arity restriction of custom behaviors.
Ryo Nakamura

Support :url_safe option for MessageEncryptor.
The MessageEncryptor constructor now accepts a :url_safe option, similar
to the MessageVerifier constructor.  When enabled, this option ensures
that messages use a URL-safe encoding.
Jonathan Hefner

Add url_safe option to ActiveSupport::MessageVerifier initializer
ActiveSupport::MessageVerifier.new now takes optional url_safe argument.
It can generate URL-safe strings by passing url_safe: true.
verifier = ActiveSupport::MessageVerifier.new(url_safe: true)
message = verifier.generate(data) # => URL-safe string

This option is false by default to be backwards compatible.
Shouichi Kamiya

Enable connection pooling by default for MemCacheStore and RedisCacheStore.
If you want to disable connection pooling, set :pool option to false when configuring the cache store:
config.cache_store = :mem_cache_store, "cache.example.com", pool: false

fatkodima

Add force: support to ActiveSupport::Cache::Store#fetch_multi.
fatkodima

Deprecated :pool_size and :pool_timeout options for configuring connection pooling in cache stores.
Use pool: true to enable pooling with default settings:
config.cache_store = :redis_cache_store, pool: true

Or pass individual options via :pool option:
config.cache_store = :redis_cache_store, pool: { size: 10, timeout: 2 }

fatkodima

Allow #increment and #decrement methods of ActiveSupport::Cache::Store
subclasses to set new values.
Previously incrementing or decrementing an unset key would fail and return
nil. A default will now be assumed and the key will be created.
Andrej Blagojević, Eugene Kenny

Add skip_nil: support to RedisCacheStore
Joey Paris

ActiveSupport::Cache::MemoryStore#write(name, val, unless_exist:true) now
correctly writes expired keys.
Alan Savage

ActiveSupport::ErrorReporter now accepts and forward a source: parameter.
This allow libraries to signal the origin of the errors, and reporters
to easily ignore some sources.
Jean Boussier

Fix and add protections for XSS in ActionView::Helpers and ERB::Util.
Add the method ERB::Util.xml_name_escape to escape dangerous characters
in names of tags and names of attributes, following the specification of XML.
Álvaro Martín Fraguas

Respect ActiveSupport::Logger.new's :formatter keyword argument
The stdlib Logger::new allows passing a :formatter keyword argument to
set the logger's formatter. Previously ActiveSupport::Logger.new ignored
that argument by always setting the formatter to an instance of
ActiveSupport::Logger::SimpleFormatter.
Steven Harman

Deprecate preserving the pre-Ruby 2.4 behavior of to_time
With Ruby 2.4+ the default for +to_time+ changed from converting to the
local system time to preserving the offset of the receiver. At the time Rails
supported older versions of Ruby so a compatibility layer was added to assist
in the migration process. From Rails 5.0 new applications have defaulted to
the Ruby 2.4+ behavior and since Rails 7.0 now only supports Ruby 2.7+
this compatibility layer can be safely removed.
To minimize any noise generated the deprecation warning only appears when the
setting is configured to false as that is the only scenario where the
removal of the compatibility layer has any effect.
Andrew White

Pathname.blank? only returns true for Pathname.new("")
Previously it would end up calling Pathname#empty? which returned true
if the path existed and was an empty directory or file.
That behavior was unlikely to be expected.
Jean Boussier

Deprecate Notification::Event's #children and #parent_of?
John Hawthorn

Change the default serializer of ActiveSupport::MessageVerifier from
Marshal to ActiveSupport::JSON when using config.load_defaults 7.1.
Messages serialized with Marshal can still be read, but new messages will
be serialized with ActiveSupport::JSON. For more information, see
https://guides.rubyonrails.org/v7.1/configuring.html#config-active-support-message-serializer.
Saba Kiaei, David Buckley, and Jonathan Hefner

Change the default serializer of ActiveSupport::MessageEncryptor from
Marshal to ActiveSupport::JSON when using config.load_defaults 7.1.
Messages serialized with Marshal can still be read, but new messages will
be serialized with ActiveSupport::JSON. For more information, see
https://guides.rubyonrails.org/v7.1/configuring.html#config-active-support-message-serializer.
Zack Deveau, Martin Gingras, and Jonathan Hefner

Add ActiveSupport::TestCase#stub_const to stub a constant for the duration of a yield.
DHH

Fix ActiveSupport::EncryptedConfiguration to be compatible with Psych 4
Stephen Sugden

Improve File.atomic_write error handling
Daniel Pepper

Fix Class#descendants and DescendantsTracker#descendants compatibility with Ruby 3.1.
The native Class#descendants was reverted prior to Ruby 3.1 release,
but Class#subclasses was kept, breaking the feature detection.
Jean Boussier

Active Model

Remove change in the typography of user facing error messages.
For example, “can’t be blank” is again “can't be blank”.
Rafael Mendonça França

Support composite identifiers in to_key
to_key avoids wrapping #id value into an Array if #id already an array
Nikita Vasilevsky

Add ActiveModel::Conversion.param_delimiter to configure delimiter being used in to_param
Nikita Vasilevsky

undefine_attribute_methods undefines alias attribute methods along with attribute methods.
Nikita Vasilevsky

Error.full_message now strips ":base" from the message.
zzak

Add a load hook for ActiveModel::Model (named active_model) to match the load hook for
ActiveRecord::Base and allow for overriding aspects of the ActiveModel::Model class.
Lewis Buckley

Improve password length validation in ActiveModel::SecurePassword to consider byte size for BCrypt
compatibility.
The previous password length validation only considered the character count, which may not
accurately reflect the 72-byte size limit imposed by BCrypt. This change updates the validation
to consider both character count and byte size while keeping the character length validation in place.
user = User.new(password: "a" * 73)  # 73 characters
user.valid? # => false
user.errors[:password] # => ["is too long"]

user = User.new(password: "あ" * 25)  # 25 characters, 75 bytes
user.valid? # => false
user.errors[:password] # => ["is too long"]

ChatGPT, Guillermo Iguaran

has_secure_password now generates an #{attribute}_salt method that returns the salt
used to compute the password digest. The salt will change whenever the password is changed,
so it can be used to create single-use password reset tokens with generates_token_for:
class User < ActiveRecord::Base
  has_secure_password

  generates_token_for :password_reset, expires_in: 15.minutes do
    password_salt&.last(10)
  end
end

Lázaro Nixon

Improve typography of user facing error messages. In English contractions,
the Unicode APOSTROPHE (U+0027) is now RIGHT SINGLE QUOTATION MARK
(U+2019). For example, "can't be blank" is now "can’t be blank".
Jon Dufresne

Add class to ActiveModel::MissingAttributeError error message.
Show which class is missing the attribute in the error message:
user = User.first
user.pets.select(:id).first.user_id

=> ActiveModel::MissingAttributeError: missing attribute 'user_id' for Pet
```

*Petrik de Heus*

Raise NoMethodError in ActiveModel::Type::Value#as_json to avoid unpredictable
results.
Vasiliy Ermolovich

Custom attribute types that inherit from Active Model built-in types and do
not override the serialize method will now benefit from an optimization
when serializing attribute values for the database.
For example, with a custom type like the following:
class DowncasedString < ActiveModel::Type::String
  def cast(value)
    super&.downcase
  end
end

ActiveRecord::Type.register(:downcased_string, DowncasedString)

class User < ActiveRecord::Base
  attribute :email, :downcased_string
end

user = User.new(email<EMAIL_ADDRESS>
Serializing the email attribute for the database will be roughly twice as
fast.  More expensive cast operations will likely see greater improvements.
Jonathan Hefner

has_secure_password now supports password challenges via a
password_challenge accessor and validation.
A password challenge is a safeguard to verify that the current user is
actually the password owner.  It can be used when changing sensitive model
fields, such as the password itself.  It is different than a password
confirmation, which is used to prevent password typos.
When password_challenge is set, the validation checks that the value's
digest matches the currently persisted password_digest (i.e.
password_digest_was).
This allows a password challenge to be done as part of a typical update
call, just like a password confirmation.  It also allows a password
challenge error to be handled in the same way as other validation errors.
For example, in the controller, instead of:
password_params = params.require(:password).permit(
  :password_challenge,
  :password,
  :password_confirmation,
)

password_challenge = password_params.delete(:password_challenge)
@password_challenge_failed = !current_user.authenticate(password_challenge)

if !@password_challenge_failed && current_user.update(password_params)

...
end
```

You can now write:

```ruby
password_params = params.require(:password).permit(
  :password_challenge,
  :password,
  :password_confirmation,
).with_defaults(password_challenge: "")

if current_user.update(password_params)

...
end
```

And, in the view, instead of checking `@password_challenge_failed`, you can
render an error for the `password_challenge` field just as you would for
other form fields, including utilizing `config.action_view.field_error_proc`.

*Jonathan Hefner*

Support infinite ranges for LengthValidators :in/:within options
validates_length_of :first_name, in: ..30

fatkodima

Add support for beginless ranges to inclusivity/exclusivity validators:
validates_inclusion_of :birth_date, in: -> { (..Date.today) }

validates_exclusion_of :birth_date, in: -> { (..Date.today) }

Bo Jeanes

Make validators accept lambdas without record argument

Before
validates_comparison_of :birth_date, less_than_or_equal_to: ->(_record) { Date.today }

After
validates_comparison_of :birth_date, less_than_or_equal_to: -> { Date.today }
```

*fatkodima*

Fix casting long strings to Date, Time or DateTime
fatkodima

Use different cache namespace for proxy calls
Models can currently have different attribute bodies for the same method
names, leading to conflicts. Adding a new namespace :active_model_proxy
fixes the issue.
Chris Salzberg

Active Record

Remove -shm and -wal SQLite files when rails db:drop is run.
Niklas Häusele

Revert the change to raise an ArgumentError when #accepts_nested_attributes_for is declared more than once for
an association in the same class.
The reverted behavior broke the case where the #accepts_nested_attributes_for was defined in a concern and
where overridden in the class that included the concern.
Rafael Mendonça França

Better naming for unique constraints support.
Naming unique keys leads to misunderstanding it's a short-hand of unique indexes.
Just naming it unique constraints is not misleading.
In Rails 7.1.0.beta1 or before:
add_unique_key :sections, [:position], deferrable: :deferred, name: "unique_section_position"
remove_unique_key :sections, name: "unique_section_position"

Now:
add_unique_constraint :sections, [:position], deferrable: :deferred, name: "unique_section_position"
remove_unique_constraint :sections, name: "unique_section_position"

Ryuta Kamizono

Fix duplicate quoting for check constraint expressions in schema dump when using MySQL
A check constraint with an expression, that already contains quotes, lead to an invalid schema
dump with the mysql2 adapter.
Fixes #42424.
Felix Tscheulin

Performance tune the SQLite3 adapter connection configuration
For Rails applications, the Write-Ahead-Log in normal syncing mode with a capped journal size, a healthy shared memory buffer and a shared cache will perform, on average, 2× better.
Stephen Margheim

Allow SQLite3 busy_handler to be configured with simple max number of retries
Retrying busy connections without delay is a preferred practice for performance-sensitive applications. Add support for a database.yml retries integer, which is used in a simple busy_handler function to retry busy connections without exponential backoff up to the max number of retries.
Stephen Margheim

The SQLite3 adapter now supports supports_insert_returning?
Implementing the full supports_insert_returning? contract means the SQLite3 adapter supports auto-populated columns (#48241) as well as custom primary keys.
Stephen Margheim

Ensure the SQLite3 adapter handles default functions with the || concatenation operator
Previously, this default function would produce the static string "'Ruby ' || 'on ' || 'Rails'".
Now, the adapter will appropriately receive and use "Ruby on Rails".
change_column_default "test_models", "ruby_on_rails", -> { "('Ruby ' || 'on ' || 'Rails')" }

Stephen Margheim

Dump PostgreSQL schemas as part of the schema dump.
Lachlan Sylvester

Encryption now supports support_unencrypted_data being set per-attribute.
You can now opt out of support_unencrypted_data on a specific encrypted attribute.
This only has an effect if ActiveRecord::Encryption.config.support_unencrypted_data == true.
class User < ActiveRecord::Base
  encrypts :name, deterministic: true, support_unencrypted_data: false
  encrypts :email, deterministic: true
end

Alex Ghiculescu

Add instrumentation for Active Record transactions
Allows subscribing to transaction events for tracking/instrumentation. The event payload contains the connection and the outcome (commit, rollback, restart, incomplete), as well as timing details.
ActiveSupport::Notifications.subscribe("transaction.active_record") do |event|
  puts "Transaction event occurred!"
  connection = event.payload[:connection]
  puts "Connection: #{connection.inspect}"
end

Daniel Colson, Ian Candy

Support composite foreign keys via migration helpers.

Assuming "carts" table has "(shop_id, user_id)" as a primary key.
add_foreign_key(:orders, :carts, primary_key: [:shop_id, :user_id])

remove_foreign_key(:orders, :carts, primary_key: [:shop_id, :user_id])
foreign_key_exists?(:orders, :carts, primary_key: [:shop_id, :user_id])
```

*fatkodima*

Adds support for if_not_exists when adding a check constraint.
add_check_constraint :posts, "post_type IN ('blog', 'comment', 'share')", if_not_exists: true

Cody Cutrer

Raise an ArgumentError when #accepts_nested_attributes_for is declared more than once for an association in
the same class. Previously, the last declaration would silently override the previous one. Overriding in a subclass
is still allowed.
Joshua Young

Deprecate rewhere argument on #merge.
The rewhere argument on #mergeis deprecated without replacement and
will be removed in Rails 7.2.
Adam Hess

Fix unscope is not working in specific case
Before:
Post.where(id: 1...3).unscope(where: :id).to_sql # "SELECT `posts`.* FROM `posts` WHERE `posts`.`id` >= 1 AND `posts`.`id` < 3"

After:
Post.where(id: 1...3).unscope(where: :id).to_sql # "SELECT `posts`.* FROM `posts`"

Fixes #48094.
Kazuya Hatanaka

Change has_secure_token default to on: :initialize
Change the new default value from on: :create to on: :initialize
Can be controlled by the config.active_record.generate_secure_token_on
configuration:
config.active_record.generate_secure_token_on = :create

Sean Doyle

Fix change_column not setting precision: 6 on datetime columns when
using 7.0+ Migrations and SQLite.
Hartley McGuire

Support composite identifiers in to_key
to_key avoids wrapping #id value into an Array if #id already an array
Nikita Vasilevsky

Add validation option for enum
class Contract < ApplicationRecord
  enum :status, %w[in_progress completed], validate: true
end
Contract.new(status: "unknown").valid? # => false
Contract.new(status: nil).valid? # => false
Contract.new(status: "completed").valid? # => true

class Contract < ApplicationRecord
  enum :status, %w[in_progress completed], validate: { allow_nil: true }
end
Contract.new(status: "unknown").valid? # => false
Contract.new(status: nil).valid? # => true
Contract.new(status: "completed").valid? # => true

Edem Topuzov, Ryuta Kamizono

Allow batching methods to use already loaded relation if available
Calling batch methods on already loaded relations will use the records previously loaded instead of retrieving
them from the database again.
Adam Hess

Deprecate read_attribute(:id) returning the primary key if the primary key is not :id.
Starting in Rails 7.2, read_attribute(:id) will return the value of the id column, regardless of the model's
primary key. To retrieve the value of the primary key, use #id instead. read_attribute(:id) for composite
primary key models will now return the value of the id column.
Adrianna Chang

Fix change_table setting datetime precision for 6.1 Migrations
Hartley McGuire

Fix change_column setting datetime precision for 6.1 Migrations
Hartley McGuire

Add ActiveRecord::Base#id_value alias to access the raw value of a record's id column.
This alias is only provided for models that declare an :id column.
Adrianna Chang

Fix previous change tracking for ActiveRecord::Store when using a column with JSON structured database type
Before, the methods to access the changes made during the last save #saved_change_to_key?, #saved_change_to_key, and #key_before_last_save did not work if the store was defined as a store_accessor on a column with a JSON structured database type
Robert DiMartino

Fully support NULLS [NOT] DISTINCT for PostgreSQL 15+ indexes.
Previous work was done to allow the index to be created in a migration, but it was not
supported in schema.rb. Additionally, the matching for NULLS [NOT] DISTINCT was not
in the correct order, which could have resulted in inconsistent schema detection.
Gregory Jones

Allow escaping of literal colon characters in sanitize_sql_* methods when named bind variables are used
Justin Bull

Fix #previously_new_record? to return true for destroyed records.
Before, if a record was created and then destroyed, #previously_new_record? would return true.
Now, any UPDATE or DELETE to a record is considered a change, and will result in #previously_new_record?
returning false.
Adrianna Chang

Specify callback in has_secure_token
class User < ApplicationRecord
  has_secure_token on: :initialize
end

User.new.token # => "abc123...."

Sean Doyle

Fix incrementation of in memory counter caches when associations overlap
When two associations had a similarly named counter cache column, Active Record
could sometime increment the wrong one.
Jacopo Beschi, Jean Boussier

Don't show secrets for Active Record's Cipher::Aes256Gcm#inspect.
Before:
ActiveRecord::Encryption::Cipher::Aes256Gcm.new(secret).inspect
"#<ActiveRecord::Encryption::Cipher::Aes256Gcm:0x0000000104888038 ... @secret=\"\\xAF\\bFh]LV}q\\nl\\xB2U\\xB3 ... >"

After:
ActiveRecord::Encryption::Cipher::Aes256Gcm(secret).inspect
"#<ActiveRecord::Encryption::Cipher::Aes256Gcm:0x0000000104888038>"

Petrik de Heus

Bring back the historical behavior of committing transaction on non-local return.
Model.transaction do
  model.save
  return
  other_model.save # not executed
end

Historically only raised errors would trigger a rollback, but in Ruby 2.3, the timeout library
started using throw to interrupt execution which had the adverse effect of committing open transactions.
To solve this, in Active Record 6.1 the behavior was changed to instead rollback the transaction as it was safer
than to potentially commit an incomplete transaction.
Using return, break or throw inside a transaction block was essentially deprecated from Rails 6.1 onwards.
However with the release of timeout 0.4.0, Timeout.timeout now raises an error again, and Active Record is able
to return to its original, less surprising, behavior.
This historical behavior can now be opt-ed in via:
Rails.application.config.active_record.commit_transaction_on_non_local_return = true

And is the default for new applications created in Rails 7.1.
Jean Boussier

Deprecate name argument on #remove_connection.
The name argument is deprecated on #remove_connection without replacement. #remove_connection should be called directly on the class that established the connection.
Eileen M. Uchitelle

Fix has_one through singular building with inverse.
Allows building of records from an association with a has_one through a
singular association with inverse. For belongs_to through associations,
linking the foreign key to the primary key model isn't needed.
For has_one, we cannot build records due to the association not being mutable.
Gannon McGibbon

Disable database prepared statements when query logs are enabled
Prepared Statements and Query Logs are incompatible features due to query logs making every query unique.
zzak, Jean Boussier

Support decrypting data encrypted non-deterministically with a SHA1 hash digest.
This adds a new Active Record encryption option to support decrypting data encrypted
non-deterministically with a SHA1 hash digest:
Rails.application.config.active_record.encryption.support_sha1_for_non_deterministic_encryption = true

The new option addresses a problem when upgrading from 7.0 to 7.1. Due to a bug in how Active Record
Encryption was getting initialized, the key provider used for non-deterministic encryption were using
SHA-1 as its digest class, instead of the one configured globally by Rails via
Rails.application.config.active_support.key_generator_hash_digest_class.
Cadu Ribeiro and Jorge Manrubia

Added PostgreSQL migration commands for enum rename, add value, and rename value.
rename_enum and rename_enum_value are reversible. Due to Postgres
limitation, add_enum_value is not reversible since you cannot delete enum
values. As an alternative you should drop and recreate the enum entirely.
rename_enum :article_status, to: :article_state

add_enum_value :article_state, "archived" # will be at the end of existing values
add_enum_value :article_state, "in review", before: "published"
add_enum_value :article_state, "approved", after: "in review"

rename_enum_value :article_state, from: "archived", to: "deleted"

Ray Faddis

Allow composite primary key to be derived from schema
Booting an application with a schema that contains composite primary keys
will not issue warning and won't nilify the ActiveRecord::Base#primary_key value anymore.
Given a travel_routes table definition and a TravelRoute model like:
create_table :travel_routes, primary_key: [:origin, :destination], force: true do |t|
  t.string :origin
  t.string :destination
end

class TravelRoute < ActiveRecord::Base; end

The TravelRoute.primary_key value will be automatically derived to ["origin", "destination"]
Nikita Vasilevsky

Include the connection_pool with exceptions raised from an adapter.
The connection_pool provides added context such as the connection used
that led to the exception as well as which role and shard.
Luan Vieira

Support multiple column ordering for find_each, find_in_batches and in_batches.
When find_each/find_in_batches/in_batches are performed on a table with composite primary keys, ascending or descending order can be selected for each key.
Person.find_each(order: [:desc, :asc]) do |person|
  person.party_all_night!
end

Takuya Kurimoto

Fix where on association with has_one/has_many polymorphic r

Configuration
📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.

[ ] If you want to rebase/retry this PR, check this box

This PR has been generated by Renovate Bot.

⚠ Artifact update problem
Renovate failed to update artifacts related to this branch. You probably do not want to merge this PR as-is.
♻ Renovate will retry this branch, including artifacts, only when one of the following happens:

any of the package files in this branch needs updating, or
the branch becomes conflicted, or
you click the rebase/retry checkbox if found above, or
you rename this PR's title to start with "rebase!" to trigger it manually

The artifact failure details are included below:
File name: example/subscriber/Gemfile.lock
Fetching gem metadata from https://rubygems.org/..........
Resolving dependencies...

Bundler could not find compatible versions for gem "rails":
  In Gemfile:
    rails (~> 7.0.0)

    trabox was resolved to 0.1.2, which depends on
      rails (~> 6.0, >= 6.0.0)

File name: example/relayer/Gemfile.lock
Fetching gem metadata from https://rubygems.org/..........
Resolving dependencies...

Bundler could not find compatible versions for gem "rails":
  In Gemfile:
    rails (~> 7.0.0)

    trabox was resolved to 0.1.2, which depends on
      rails (~> 6.0, >= 6.0.0)

Edited/Blocked Notification
Renovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR.
You can manually request rebase by checking the rebase/retry box above.
⚠ Warning: custom changes will be lost.


App Analytics dashboards

Overview
Application Analytics allows OpenSearch Dashboards users to select logs, traces, and metrics to be part of an application that can be monitored for overall health and visualized on one page. It also allows users to quickly pivot between traces, metrics, and logs to dig into the source of any issues that arise.
Problem
Currently, users have no way of identifying which trace and log data belongs to which application because all trace data ends up in the same index and log data goes into different independent indices. This application is made up of user-selected logs, metrics, and traces. The current solution forces users to sift through large volumes of data to get to what they really need. Also this data, distributed across Trace Analytics, Event Analytics, Dashboards, does not provide a comprehensive view of an application’s health at a glance.
Requirements
Home Page

Users are able to view existing applications and some high level information (Name, Composition, Current Availability, Availability Metrics)
Users are able to create/rename/duplicate/delete applications

Create Page
Application Information

Users are able to specify name of new application
Users are able to optionally provide a description of their application

Composition

Users are able to configure base query
Users are able to select services and entities to include in the application
Users are able to constrain application to specific trace groups

Availability

Users are able to set custom levels of availability according to query conditions

Application Details
Overview Tab

Users are able to view Latency by Trace Group
Users are able to view default charts: Application Composition Map,  Error Rate, Throughput
Users are able to add new charts

Services Tab

Users are able to view information about specific or all services in a table

Traces & Spans Tab

Users are able to view general information about traces and spans in two tables
Users are able to view more detailed information by selecting a trace or span

Log Events

Users are able to query and analyze logs for their application

Configuration

Users are able to edit their application composition and availability settings

UX/UI Design
Figma Link
Home Page

Overview Tab

Services Tab

Configuration Tab

Development Tasks
Home Page

[x] Add table to view existing applications

[ ] #376

[x] #340

[x] Add a button to create a new application

[x] Redirect to Create Page if clicked

[x] #326

[x] #327

[ ] #328

[ ] #343

[ ] Redirect to Application Details for selected application

Create Page

[x] Add create button

[x] #319

[x] #329

[ ] #323

[ ] #324

[ ] #377

[x] Add cancel button

[x] #325

Application Information

[x] Add text form fields for name(required) and description(optional)

[x] #288

Composition

[x] Add accordion for Log Sources

[x] #292

[x] #289

[x] #290

[x] #291

[x] #305

[x] #320

[x] Add accordion for Services & entities

[x] Add select form field where users can select services and entities

[x] #293

[x] #304

[x] #321

[x] Add accordion for Trace groups

[x] #295

[x] #296

[x] #308

[x] #322

Availability

[ ] Add ability to create PPL expressions as conditions for availability levels

[ ] Add functionality to truncate long PPL expressions

[ ] Make sure that If otherwise is only a condition for one level

[ ] Add autocomplete

[ ] Add drag and drop to move levels up and down
[ ] Add buttons to delete levels
[ ] Add popover button to add custom level

[ ] Add color picker

[ ] Add default availability colors

[ ] Add text form field for label

[ ] Add buttons to create/cancel

[ ] Add button to cancel

[ ] Redirect to Home Page if clicked

[ ] Add button to create application

[ ] Redirect to Application Details page for created application

Application Details
Overview Tab (Dashboard from Trace Analytics)

[ ] Modularize to only include trace groups and services that are part of application
[ ] Add button to add new chart
[x] Add table to view latency of trace groups

[x] Add filter for < 95th percentile or >= 95th percentile

[ ] Modularize to add ability to change benchmark

[x] Add pop up for enlarged view of 24-hour latency trend

[x] Make Traces column data value a link

[x] Redirect to Traces & Spans tab with filter of trace group

[x] Make Trace group name column data value a link (Possibly remove this)

[ ] Filter by selected trace group

[x] Add Application Composition Map chart

[ ] Modularize to only include selected services

[ ] Modularize to not show Focus on search bar

[ ] Modularize to change name of chart from Service Map → Application Composition Map

[x] Add Error Rate chart
[x] Add Throughput chart

Services Tab (Services from Trace Analytics)

[ ] Modularize to only include services that are part of application
[ ] Add filter bar

[ ] Modularize filter bar to have placeholder text: Service Name

[ ] Add table to view services

[ ] Make Name column data value a link

[ ] Redirect to service detail page / open side popup with details

[ ] Make Traces column data value a link

[ ] Redirect to Traces & Spans tab with filter of trace group

Traces & Spans Tab (Traces and Span Details Table from Trace Analytics)

[x] #309

[x] Filter traces and spans by trace id, trace group name, or service name

[ ] Add table to view Traces

[ ] Modularize to include Services, Compared to this time last week columns

[ ] Make Trace ID column a link

[ ] Modularize to open slide-out of trace details instead of trace details page when clicked

[ ] Add trace details slide-out (Trace View from Trace Analytics)

[ ] Add Overview panel

[ ] Modularize to include Compared to this time last week

[ ] Modularize to make Span bar chart selectable

[ ] Redirect to Span details Panel when clicked

[ ] Add table to view Spans

[ ] Modularize to exclude service column

[ ] Make Span ID column a link

[x] Open slide-out of span details (Span Detail Flyout from Trace Analytics)

[ ] Modularize to include Payload Event panel

Log Events (Log Explorer from Event Analytics)

[ ] Modularize to only include selected indices
[ ] Modularize to not have the save button
[ ] Add PPL Query search bar

[ ] Add read-only Base Query

[ ] Popover with full base query on hover

[ ] Filter logs according to PPL Query

[ ] Change component to open side slide-out of log details

Configuration

[ ] Add Header with Application Composition
[ ] Add Edit button

[ ] Redirect to create page in edit mode when clicked

[ ] Show current Composition
[ ] Show current Availability

Solution
Relationship to Other Modules
Dashboard, Trace, Span Detail, Service from Trace Analytics and Log Explorer from Event Analytics will be re-used in Application Analytics. We will need to refactor the components to become more modular. We also need to make sure only the selected services are filtered through when displaying information.

Data Model
Application

name: User-defined name of application
description (optional): User-defined description of the application
baseQuery: PPL query to filter the application by
servicesEntities: List of services and entities included in application
traceGroups: List of trace groups included in application
( - availabilityLevels: User-defined thresholds that signify custom levels of availability of application

label
levelPriority
color
condition ) Temporarily not added

Model
{
   "properties": {
        "name": {
            "type": "keyword",
            },
        "description": {
            "type": "text",
            },
        "baseQuery": {
            "type": "text",
            },
        "servicesEntities": {
            "type": "nested",
        },
        "traceGroups": {
            "type": "nested",
        },
    }
}

Example
{
  "properties": {
    "name": "Application 1",
    "description": "This is my app",
    "baseQuery": "source = flight_logs",
    "servicesEntities": ["Payment"],
    "traceGroups" : ["Payment.auto"]
    }
}

Options for Structure of Availability Levels
Option 1 - List of objects with all of the fields
Pros: Simple
"availabilityLevels": [
    {
    "label": "Available",
    "color": "#50C878",
    "condition": "If otherwise",
    "levelPriority": 0,
    }, {
    "label": "Unavailable",
    "color": "#FF0000",
    "condition": "when errorRate() >= 2",
    "levelPriority": 1,
    },
]

Option 2 - List of objects with key/value pairs. (key: levelPriority) (value: label, color condition in object)
Pros: Easier to sort,
"availabilityLevels": [
    0: {
    "content": {
        "label": "Available",
        "color": "#50C878",
        "condition": "If otherwise",
        }
    },
    1: {
    "content": {
        "label": "Unavailable",
        "color": "#FF0000",
        "condition": "when errorRate() >= 2",
        }
    },
]

API Design
APP_ANALYTICS_API_PREFIX='/api/observability/application'

Fetch all Applications

GET ${APP_ANALYTICS_API_PREFIX}/

RESPONSE BODY
{
  "statusCode": 200,
  "message": "Ready",
  "applications": [
    {
      "id": "2FF3GW3H8",
      "name": "Application 1",
      "servicesEntities": ["Payment", "Users"],
      "traceGroups" : ["Payment.auto", "Users.admin"]
    },
    {
      "id": "2FHEP953H",
      "name": "Application 2",
      "servicesEntities": ["Purchase"],
      "traceGroups" : ["Purchase.source"]
    }
  ]
}

Fetch Application by ID

GET ${APP_ANALYTICS_API_PREFIX}/{application_id}

    RESPONSE BODY
    {
      "statusCode": 200,
      "message": "Application Fetched",
      "application": {
            "name": "Application 1",
            "description": "Description for the application",
            "base_query": "source = opensearch_sample_database_flights",
            "services_entities": [
                "Payment",
                "Users",
                "Purchase"
            ]
            "trace_groups": [
                "Payment.auto",
                "Users.admin",
                "Purchase.source",
            ]
        }
    }

Create Application

POST ${APP_ANALYTICS_API_PREFIX}/

    REQUEST BODY
    {
       "name": "Application 1",
        "description": "Description for the application",
        "base_query": "source = opensearch_sample_database_flights",
        "services_entities": [
            "Payment",
            "Users",
            "Purchase"
        ]
        "trace_groups": [
            "Payment.auto",
            "Users.admin",
            "Purchase.source",
        ]
    }

    RESPONSE BODY
    {
      "statusCode": 200,
      "message": "Application Created",
      "newAppId": "2FG6FWGY5" // New application ID
    }

Update Application - all fields are updatable

PATCH ${APP_ANALYTICS_API_PREFIX}/{application_id}

    REQUEST BODY
    {
      "appId": "2FG6FWGY5", // application id to be renamed
      "base_query": "source = opensearch_sample_database_flights" // fields to update
    }

    RESPONSE BODY
    {
      "statusCode": 200,
      "message": "Application Updated"
    }

Duplicate Application

POST ${APP_ANALYTICS_API_PREFIX}/clone

    REQUEST BODY
    {
      "appId": "2FG6FWGY5", // application id to be renamed
      "name":"Application 1 (copy)" // new name for the application to be duplicated
    }

    RESPONSE BODY
    {
      "statusCode": 200,
      "message": "Application Cloned",
      "newAppId": "2FFAG7HAQ" // newly duplicated application id
    }

Delete Application

DELETE ${APP_ANALYTICS_API_PREFIX}/{application_id}

    RESPONSE BODY
    {
      "statusCode": 204,
      "message": "Application Deleted",
    }

Autocomplete Logic
Possible PPL commands: source, dedup, eval, fields, head, rare, rename, sort, stats, top, where
for Base Query on Create Page

Not allowed commands: stats, head, rare, top
ex:   source = opensearch_dashboard_sample_flights | where DestCityName = 'Venice'

for Availability Level Condition on Create Page (Conditions can not use PPL query)

Allowed commands: where, eval, rename
Condition must be evaluate to boolean

for Filter on Log Events Tab

Not allowed commands: source
ex:  where OriginCityName = 'Rome' | stats avg( AvgTicketPrice)

for Filter on Traces & Spans Tab (pseudo) (Using filter instead of PPL query)

@anirudha
Are these solution patterns being developed with multi-tenancy and various RBAC in mind?
Are there issues and/or other docs that could be linked back to on these solution patterns?

yes, the observabilty plugin supports RBAC  and multi -tenancy using the security plugin

@anirudha additionally will the plugin support customizable index patterns, etc.. ?
The trace analytics plug-in required use of otel-v1-apm-span-* and otel-v1-apm-service-map*, which will not work in all environments, especially when multi-tenancy comes into play.

@ryn9 Hi Ryan, I talked to Ani about your question. We are planning on supporting regex based index matching in PPL soon, I think he said next release. As to your second question, I'm a little confused how multi-tenancy will affect multiple index matching? I think the security plugin should handle any access restrictions for indices. Please feel free to elaborate.

@eugenesk24

As to your second question, I'm a little confused how multi-tenancy will affect multiple index matching?
I was trying to say that making specific index patterns required for plugin use can have impact on what is, probably, the most common way to handle multi-tenancy + RBAC - role based access to specific indices. So - I am hoping that static index names are not being considered like they were with the initial trace analytics plugin.

To note - plugins haven't always been thought with multi-tenancy + RBAC in mind - so I am hoping to find out more details about how the observability plugin is being designed with this in mind.

Hi @ryn9 ,
Could you please elaborate on a use-case you are looking for in some details, for now here are the details.

Observability plugin support same tenancy structures as all of dashboards eg. link
RBAC is i.e roles based access control will follow the rules configured by your custer admin on the opensearch setup.
Currently we don't support index patterns for wildcard indices, this will be a supported in the next few releases. The wildcards will match any permission-ed index available to user for this roles.

The trace analytics plug-in required use of otel-v1-apm-span-* and otel-v1-apm-service-map*, which will not work in all environments, especially when multi-tenancy comes into play.
could you explain this with an example. From what i know access to these are only based on your roles and not tenancy.

could you explain this with an example. From what i know access to these are only based on your roles and not tenancy.
Sorry to confuse - 'tenancy' was probably the wrong word here.

The trace analytics plug-in required use of otel-v1-apm-span-* and otel-v1-apm-service-map*, which will not work in all environments. For example - we support different groups, whom (via role access) should only have access to certain data.  IE.... we wouldn't want let userA and userB both have access to these index patterns, as we don't want to allow them access to each other's data.

Hi
Same problem here : https://discuss.opendistrocommunity.dev/t/trace-analysis-and-multi-tenant/4602/2
Trace Analysis plugin require indices:data/read/search permission on all otel-v1-apm-span-* indices
so either you user have permissions for all indices, or nothing works
I think that supporting index patterns for wildcard indices is the solution.

@anirudha

Currently we don't support index patterns for wildcard indices, this will be a supported in the next few releases. The wildcards will match any permission-ed index available to user for this roles.

Does that mean the plugins would also be moving away from requiring specific index names?
Perhaps create different 'views' that, upon creation, could use different index patterns?

@anirudha @eugenesk24 - wondering if you folks have confirmed index pattern support for PPL for 1.3 yet.
It doesn't answer all of the questions above, but I believe is an important step for PPL adoption.

Sorry for the late reply @ryn9 ! We have added multiple index use through comma-separate indices and wildcard matching.

@eugenesk24 .. awesome. I am excited to see it :)

Merged to main for 1.3 release


Rewrite the mpsc channels

Previously, the bounded mpsc channels wouldn't provide backpressure
in many cases because they provided a guaranteed slot for every
sender. This change refactors mpsc channels to never allow more than
buffer + 1 messages in-flight at a time.
Senders will attempt to increment the number of in-flight messages
in poll-ready. If successful, the count is incremented and a message
is sent. If the buffer was full, the sender adds itself to a queue
to be awoken. When a message is received, the receiver attempts to
awaken a Sender and grant it "sending rights". If no such channel
was found, it decreases the number of in-flight messages currently
present in the buffer. If a channel with "sending rights" is dropped,
it gives those sending rights to another waiting Sender or decreases
the number of in-flight messages.
There's one odd choice in this change, which was to pick buffer + 1
rather than buffer as the max number of elements. In practice, many
of the tests and a handful of real-world code relied on the ability
to have an initial element available for the first sender, and were
attempting to send into channels with a buffer size of zero. These
use-cases were all broken by this change without the change to use
buffer + 1.
Despite the buffer + 1 mitigation, this is still a breaking change
since it modifies the maximum number of elements allowed into a
channel. 0.2 has been out for a short enough time that it's probably
possible to transition without breaking existing code, but this
approach should be taken with caution. Luckily, 0.2 organized
futures-channel into a separate crate, so it could be released as
an 0.3 version of futures-channel, futures-sink, futures-util, and
the futures facade while still maintaining futures-io and futures-core
compatibility.
Fix https://github.com/rust-lang-nursery/futures-rs/issues/800
Fix https://github.com/rust-lang-nursery/net-wg/issues/11
cc @danburkert, @stjepang, @carllerche

This would address #403! :D

I have not reviewed the code at all, but based off of the description, this sounds exactly like the strategy that was first attempted and ruled out as it was deemed flawed. I will attempt to explain the reasoning here.
The problem is that this strategy assumes two things:

Generating the value is fast (usually because it already exists before channel capacity is requested). This is because once capacity is assigned to a Sender, capacity is blocked until the sender actually sends a value.
Once capacity is assigned to a Sender, a value will either be sent or the Sender will be dropped.

Unfortunately, neither of these assumptions hold true and violating either will result in reduced latency and / or deadlocks.
For example, if using HTTP/2.0, you might use the protocol support to propagate back pressure signals from your channel to a remote peer. In this case, once the Sender has capacity, messages over the network will signal to the peer that there is capacity, then the peer must generate the message, send it back over the wire, and finally reach the Sender. This is not a quick turn around.
Another example is the tower load balancer. A load balancer instance is composed of many services (which, internally use channels). The load balancer will call poll_ready on every inner service to check if the service instance is ready to handle a request. However, it will only dispatch a request to a single service. The balancer collects metrics to determine which inner services are healthier and will push more requests to inner services that respond faster. So, if an inner service takes a long time to go from NotReady -> Ready, it might be never called. i.e., the capacity assigned to the Sender will be held indefinitely, leading to deadlocks.
Leaky abstraction
The behavior of requiring a fast turn around between requesting capacity and sending a message (or dropping the handle) is a leaky abstraction. Future places no requirements on how a future gets polled after it is notified. The proposed behavior changes this, exposing the implementation detail of the sender to the future.
The problem is, middleware like the tower balancer can't assume that each Future / Service that it is generic over might have special requirements. As far as I can tell, this would make the proposed channel unusable from such contexts.
Channels and backpressure
Since this question comes up often, I probably should take this opportunity to write up the history and the background behind the current design since everybody gets confused. If there is a better general purpose channel strategy, then great (though, IMO this is not it).
There are fundamentally two dimensions in which channel usage need to be bounded:

The rate at which individual Sender call sites send messages into the channel.
The total number of Sender call sites.

This channel implementation only bounds the first item, and I will argue that it is incorrect to attempt to bound the second one as this responsibility lies elsewhere.
Blocking channels
Lets take a step back and look at how back pressure works with your synchronous, blocking channels. With blocking channels, there can be any number of Sender clones spread out across any number of threads, each thread racing to push values into the sender.
For simplicity, lets assume that each thread only has one Sender handle. Because synchronous code is synchronous, even if more than one Sender clone is assigned to a single thread, that thread can only be attempting to send a value in one Sender clone at a time. If the channel is full, that send will block and the thread is unable to send a value in another Sender handle.
Imagine you have a channel that is bounded to 5 values and you clone the Sender handle 50 times and spawn 50 threads and each thread attempts to send a value through the Sender. The first 5 threads will successfully send and the other 45 threads will block on the send. But, the value being sent will already have been generated and is buffered by the thread. Also, the original 5 winners are able to loop and attempt to send another value, which will block (be buffered by the thread). So, the total number of values that were rendered in memory is 55. i.e. the channel capacity + the number of sender clones.
To solve this, the user of the blocking Sender must come up with some strategy external to the channel to limit the number of threads that are spawned (i.e. limit the number of call sites). This is not the responsibility of the channel.
Limiting the call sites
The original argument for changing the behavior is that send takes self, so you have to clone for each send. The fact is, even if this channel patch lands, it would not resolve the unbounded nature of the calling code.
Assuming that the pattern is:
tx.clone().send(foo);

This returns a future, which does no work by itself. Something has to spawn this future or push it into a vector somewhere. If nothing is limiting this logic at a higher level, there will be an unbounded number of spawn tasks or items pushed into storage somewhere. Even with the proposed change, unbounded buffering behavior remains.
In my experience, every case of Sender::clone growing out of bounds was gracefully solved by adding a limit at a higher level.
But everyone gets it wrong
Yes, it's true. It is a point of serious confusion. I would argue that this is due to using the functional combinators being really hard and less the behavior of the channel. When using combinators, one must employ an entirely functional programming style, threading all necessary state through the combinators. This is very very annoying and a common shortcut is to just clone into the combinators, resulting in this behavior.
It all gets better with async / await
Finally, async / await returns the current channel implementation to sanity as one will be able to send w/ &mut self and not require moving anymore. So, the following will "just work" as expected:
await!(self.tx.send(my_val))

And, async / await will prevent attempting to send on other Sender handles from within the same task, returning us to the same behavior as blocking channels.
TL;DR

This strategy was attempted a long time ago and it results in reduced throughput and latency.
The "lack of back pressure" problems is due to a user error (though not their fault) and not a channel problem.
Everything gets magically better with async / await.

@carllerche Thanks for your detailed reply! I agree that this solution isn't perfect for some of the reasons you describe, but I'll try and outline why I think that we can use this PR as a stepping stone to something better than the status quo. Hopefully we can start to sort out more of these issues.

For example, if using HTTP/2.0, you might use the protocol support to propagate back pressure signals from your channel to a remote peer. In this case, once the Sender has capacity, messages over the network will signal to the peer that there is capacity, then the peer must generate the message, send it back over the wire, and finally reach the Sender. This is not a quick turn around.

I agree that the backpressure implemented by this PR would not suffice for such a situation, but I don't believe that the current backpressure implementation works here either. If you want backpressure to propagate from the channel back to a remote peer, you must necessarily delay requesting a value until you can be sure that there is space available to receive and process the client's message. The current implementation doesn't allow for this type of fixed-buffer backpressure with a growable number of senders. Is there something about the way that this system currently works that makes it usable in the HTTP2 case where this PR's implementation wouldn't be usable? If you're not making use of channel backpressure to push back at a higher level, then is there a reason for not using the UnboundedXXX channel types?

There are fundamentally two dimensions in which channel usage need to be bounded:

The rate at which individual Sender call sites send messages into the channel.
The total number of Sender call sites.
This channel implementation only bounds the first item, and I will argue that it is incorrect to attempt to bound the second one as this responsibility lies elsewhere.

The original argument for changing the behavior is that send takes self, so you have to clone for each send. The fact is, even if this channel patch lands, it would not resolve the unbounded nature of the calling code.

Assuming that the pattern is:
tx.clone().send(foo);
This returns a future, which does no work by itself. Something has to spawn this future or push it into a vector somewhere. If nothing is limiting this logic at a higher level, there will be an unbounded number of spawn tasks or items pushed into storage somewhere. Even with the proposed change, unbounded buffering behavior remains.

Yes, getting backpressure right in this case is something that requires a higher level of coordination. In my intended use-case, I'm reading reading requests from an OS channel and spawning the tx.clone().send(foo) tasks onto a FuturesUnordered. Once that FuturesUnodered hits a certain size, the task will stop pulling requests off of the OS channel until more of the tx.clone().send(foo) tasks complete. Before this PR, channels failed to prevent any of those tasks from completing, resulting in unbounded growth of the channel buffer. After this PR, the channels provide backpressure, meaning that fewer of the tx.clone().send(foo) tasks complete immediately, causing the FuturesUnordered to grow in size so that the OS channel is no longer read from, exerting backpressure on the underlying OS channel. As far as I can tell so far, this works just fine, but requires channels not to leak memory when cloned.
Finally, WRT "everything is fixed under async/await", I agree that async/await will help here tremendously as it reduces the number of cases in which channels must be cloned. However, there are other reasons for cloning channels apart from wanting to call self-consuming combinators-- one might want to spawn a number of 'static tasks which each contain a Sender. WIthout this PR, these cases won't correctly limit the channel to the configured buffer size.

If you want backpressure to propagate from the channel back to a remote peer, you must necessarily delay requesting a value until you can be sure that there is space available to receive and process the client's message.

This is exactly what poll_ready is for.

The current implementation doesn't allow for this type of fixed-buffer backpressure with a growable number of senders.

I'm not sure what you mean. Both implementations would support this as this is the Sink::poll_ready guarantee.

tx.clone().send(foo)

tbh, I never used the combinators personally, so I never hit the pain. However, I spoke w/ @danburkert offline a bunch about this and he explained the issue to me. The solution is a pretty simple change to the current implementation: implement Sink::poll_complete as an alias to poll_ready. This achieves the exact desired semantics. send does not complete until the value enters the channel's main buffer and not the Sender dedicated slot.

I was actually looking into this myself today, as the hyper test suite has been hitting the race condition daily with the change to a thread pool of executors. I thought maybe of just trying to patch the existing implementation, but eventually I came to think that probably we don't need yet another channel implementation in futures, when excellent work has been done on the libstd and crossbeam channels.
Specifically, they both better handle all the edge cases around multiple or single produces send at the same time a consumer is dropped.
The proposed changes I was thinking was to use either std or crossbeam's channel internally, and just add the task/waker stuff on top.

@seanmonstar

The proposed changes I was thinking was to use either std or crossbeam's channel internally, and just add the task/waker stuff on top.

That's basically what this is-- the underlying queue is basically the same as the one from std-- all the noise on top is for getting proper wakeups. It's annoyingly complicated to get right.
@carllerche

If you want backpressure to propagate from the channel back to a remote peer, you must necessarily delay requesting a value until you can be sure that there is space available to receive and process the client's message.
This is exactly what poll_ready is for.

Perhaps you've misunderstood this PR-- that's what I implemented-- poll_ready has exactly this behavior.

The current implementation doesn't allow for this type of fixed-buffer backpressure with a growable number of senders.

I'm not sure what you mean. Both implementations would support this as this is the Sink::poll_ready guarantee.

No, the current poll_ready guarantee is to only apply backpressure for buffer + num_senders slots. This PR changes it to use buffer slots, rather than adding num_senders extra. This makes it so that splitting up the work across more Senders doesn't increase the underlying number of slots.

@seanmonstar I have no opinion w.r.t the implementation as long as it maintains the current behavior and equal or greater performance. IIRC, I originally attempted to add a layer on top of std channel, but it was not possible w/o a mutex guarding both ends. I could be wrong as it was a long time ago.
@cramertj I understand what the PR is doing. I re-read the original snippet I quoted, and I think that I see what you mean.
Again, this conflates two dimensions of growth and I do not believe that the channel backlog should be used to guard against a growing number of call sites.
It seems like the crux of the problem is that the current implementation does not behave correctly when using combinators and I proposed a simple fix, what do you think about that propsal?

I'm gonna try to estate things for myself, basically repeating some of @carllerche's earlier points.
I think we can all agree with the following premise:

The goal is to limit the global number of in-flight messages destined for a given queue.

That is, when we look at the application as a whole, we want bounded "buffering", where buffering includes not just literal buffers, but also cases where a potential sender has generated a value to send but is prevented from queuing it.
That last point is key: it means that we need to coordinate not just queuing of already-produced values, but also the production of values in the first place.
The "one limit" strategy
This PR achieves our goal in the most "direct" way: by having the queue itself fully manage the global limit. To do this, it must hand out "tickets" for sending. Since the total number of tickets is bounded, so is the number of values ever produced to send (assuming senders get a ticket before producing their value).
The downside to this strategy, as @carllerche explains (and I think that @cramertj agrees), is that there is a potentially arbitrary delay between acquiring and using a ticket.
For the moment, let's focus on the potential for deadlock. As @carllerche says, reasonable abstractions elsewhere in the system may result in a given ticket for sending never being used or dropped, which can ultimately eat up the queue's capacity. @cramertj, do you agree with this potential?
The "two limits" strategy
The alternative approach, represented by the current implementation, breaks down the global limit of in-flight messages into two parts, as @carllerche articulated:

The rate at which individual Sender call sites send messages into the channel.
The total number of Sender call sites.

The first limit is imposed by the channel itself, through its limited buffer.
The second limit is imposed at a higher level in the application, where it is less likely to cause deadlock. And, in particular, this second limit will often "fall out" from other limits, e.g. on the number of active connections.
Moreover, @carllerche argues, it is not clear how to have the channel alone impose the second limit without leading to potential for deadlock and other bad inter-task interference.
Discussion
I think a core issue here is local vs global thinking. It's very tempting to think about backpressure in purely local terms: we want to avoid this particular receiver from getting too backlogged, and we do so by imposing a limit purely from its local perspective. The problem is that we can't safely impose this limit purely locally; it fundamentally involves some global coordination.
The strategy that @carllerche advocates for is one that allows individual tasks to be written in a more-or-less "isolated" style, without having to think about how e.g. grabbing a ticket may cause problems for other unrelated tasks. Global coordination is instead handled at a much higher level. That means that backpressure isn't as simple as throwing in a bounded channel, but I think in the big picture it makes life easier overall (and in particular makes abstractions more independent).
It's also worth noting that we can, and probably should, build some pure ticket abstractions, i.e. futures-aware semaphors. These can then be used in a more explicit way as one simple means of imposing global limits (where the potential for deadlock is more obvious).

I wanted to clarify a few additional remarks:
@cramertj says:

The current implementation doesn't allow for this type of fixed-buffer backpressure with a growable number of senders.

Hopefully the above clarifies, but to reiterate: the current implementation takes care of one of the two limits, and you are expected to separately coordinate the other (number of senders) more globally.
@cramertj says:

If you're not making use of channel backpressure to push back at a higher level, then is there a reason for not using the UnboundedXXX channel types?

The reason is that the bounded channel does impose an important limit: the one on the rate of sending for individual senders. It's built so that you can compose this limit with a global one on the number of senders to guarantee an overall total bound.

@carllerche

The ticket based strategy proposed in this PR does not actually solve the problem of bounding the number of callers as I explained in my long comment.

It doesn't solve bounding the number of callers, I agree. It solves a different problem: the one of callers finishing. If callers are unable to complete due to the channel being full, then it creates an increased number of callers which creates backpressure for higher levels of the system.
@aturon

For the moment, let's focus on the potential for deadlock. As @carllerche says, reasonable abstractions elsewhere in the system may result in a given ticket for sending never being used or dropped, which can ultimately eat up the queue's capacity. @cramertj, do you agree with this potential?

No-- i'd expect operations like this to have timeouts or some other mechanism to prevent them from blocking indefinitely. If we allow them to block indefinitely, then we're explicitly allowing the system to gradually leak memory over time. The purpose of backpressure in my use-case is to specifically limit the amount of memory that a process can grow to use. If we allow Senders to push items into the buffer that will never be properly consumed, nor the Sender or Receiver dropped, then we're creating a system that can leak memory.

If we allow Senders to push items into the buffer that will never be properly consumed, nor the Sender or Receiver dropped, then we're creating a system that can leak memory.

To clarify, that's not the scenario @carllerche was describing. The worry was around a grabbing a ticket for sending, but never actually completing that send.
This is why I brought up explicit semaphores: they make clear that you're juggling an abstract resource count and that while you hold a ticket you are potentially blocking other tasks.

@aturon Right, but I'm explicitly trying to block other tasks by holding this ticket.

@cramertj this implies to me that you want a higher level point of coordination. As @aturon mentioned, this semaphore can be implemented as a standalone type.
Again, I go back to the synchronous analogy. In order to prevent the value from being materialized before channel capacity is guaranteed you would need additional coordination around the blocking channel. i.e. the behavior you want is not possible in the blocking world w/ your usual channels.

Closing in favor of a more minimal set of changes, which I'll open shortly and link here.


Update to latest ubi_reader

The block alignment bug in ubi_reader should be fixed as of:
https://github.com/jrspruitt/ubi_reader/commit/63105bf3c0b1618f029da65d73e0ed7a0874e695

Included in #488.


[alpha12] Runed Smasher takes 2 durability if you break single block(when crouch)

Expected behavior:
I think it need to take 1 durability for single block

I think I've fixed this on the MysticalLib side.


Support for rx 580

hi
please add support for rx580 or if there is a way to use it tell me.
thanks.

dupe #2453