Class: DiscordStore::GuildLimits

Inherits:
Object
  • Object
show all
Defined in:
lib/discord_store/guild_limits.rb

Overview

Discovers how large an attachment this guild will actually accept.

Every previous library in this genre hardcoded the number, and every one of them broke, because Discord has moved it repeatedly and in both directions: 8 MiB for years, then 25 MB, then back to 10 MB, with boosted guilds on a different ladder again. Published write-ups from 2023 and 2026 disagree, and both were right when they were written.

So the number is treated as a fact about the running system rather than a constant. The tier table below is a conservative starting point; #probe! measures the truth by trying.

Constant Summary collapse

MIB =
1024 * 1024
TIER_LIMITS =

Deliberately pessimistic. Being wrong low costs a few extra chunks; being wrong high costs a failed upload halfway through a large file.

{
  0 => 10 * MIB,
  1 => 10 * MIB,
  2 => 50 * MIB,
  3 => 100 * MIB
}.freeze
FALLBACK_LIMIT =
8 * MIB
REQUEST_OVERHEAD =

Room for multipart framing, the payload_json part, and headers, so that a chunk sized at exactly the ceiling does not push the request over it.

16 * 1024

Instance Method Summary collapse

Constructor Details

#initialize(rest:, config:) ⇒ GuildLimits

Returns a new instance of GuildLimits.



33
34
35
36
37
# File 'lib/discord_store/guild_limits.rb', line 33

def initialize(rest:, config:)
  @rest = rest
  @config = config
  @mutex = Mutex.new
end

Instance Method Details

#attachment_limit(refresh: false) ⇒ Integer

Largest attachment this guild accepts, in bytes.

Parameters:

  • refresh (Boolean) (defaults to: false)

Returns:

  • (Integer)


43
44
45
46
47
48
# File 'lib/discord_store/guild_limits.rb', line 43

def attachment_limit(refresh: false)
  @mutex.synchronize do
    @attachment_limit = nil if refresh
    @attachment_limit ||= discover_limit
  end
end

#chunk_size ⇒ Integer

Bytes per blob chunk: the attachment ceiling less request overhead, unless the caller pinned it in configuration.

Returns:

  • (Integer)


54
55
56
# File 'lib/discord_store/guild_limits.rb', line 54

def chunk_size
  @config.chunk_size || (attachment_limit - REQUEST_OVERHEAD)
end

#probe!(channel_id:, low: MIB, high: 100 * MIB, precision: MIB / 4) ⇒ Integer

Measures the real ceiling by binary search, uploading and then deleting throwaway attachments.

Costs a handful of requests and writes garbage into the channel briefly, so it is opt-in. Run it once at deploy time and pin the result in configuration rather than probing on every boot.

Parameters:

  • channel_id (String) —

    a channel safe to write junk into

  • low (Integer) (defaults to: MIB) —

    known-good size

  • high (Integer) (defaults to: 100 * MIB) —

    known-or-suspected-bad size

  • precision (Integer) (defaults to: MIB / 4) —

    stop when the bracket is this narrow

Returns:

  • (Integer) —

    the largest size that succeeded

Raises:

  • (ArgumentError)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/discord_store/guild_limits.rb', line 70

def probe!(channel_id:, low: MIB, high: 100 * MIB, precision: MIB / 4)
  raise ArgumentError, "low must be under high" unless low < high

  best = nil

  while high - low > precision
    midpoint = low + ((high - low) / 2)

    if upload_succeeds?(channel_id, midpoint)
      best = midpoint
      low = midpoint
    else
      high = midpoint
    end
  end

  best ||= low
  @mutex.synchronize { @attachment_limit = best }
  best
end