recapi 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -2
  3. data/lib/recapi.rb +9 -4
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 28f52d6426fd83d0b14d106535a06fca9e6d29ac56b4a3b366ae31f8ca7e22df
4
- data.tar.gz: 93361e6a7d6dcbbc86be19b92ccc7fb4bc3b3695f99fa701ad08cfea17ccb57f
3
+ metadata.gz: e9d8c7e28e6929bfef64625ac5ba48ba5bec683d27b3d4c91646b67f189cadeb
4
+ data.tar.gz: b17b8ad7945fac43147ce43aca7cd27a1b62105a155fdc5ce15d8eccc7e6fca9
5
5
  SHA512:
6
- metadata.gz: 54a7bf72a2b956ed847fe57af69c3241af18c638a199a86aa3220b2f0726d495402a3faa4698e011f815f57881e5969090bf9047b28b60bc0b6d11767f88eacf
7
- data.tar.gz: a7825355e8be66451b687bc342a8c9961f8b7f8bc4c04fb7c57a38442ab59ed12daf67be7b04a39cba4b7a76843c5faec071e35dbcc15a5b3a1aba6f5154ae4b
6
+ metadata.gz: 01a65170536dc4f5f33596f509fc7102670ba72a566954b1443d7d4dc29f01137206c4a3b3c49822c4bf85bc6b3200751655a49480a2afa46fe0e10ba3fa45e5
7
+ data.tar.gz: d6d63d879a0eed194f410983abef9770b3296606184cf822a4824c56c2a0dd5a6350922595af45257c9ddf3bb3905be35de694ba9f230a9cc2d5d7a7b6676e7e
data/README.md CHANGED
@@ -30,7 +30,7 @@ line with your address): save it as `lib/recapi.rb` and
30
30
  `require Rails.root.join("lib/recapi").to_s` instead.
31
31
 
32
32
  ```sh
33
- curl -fLo lib/recapi.rb https://api.<your-domain>/sdk/ruby/recapi-0.1.9.rb
33
+ curl -fLo lib/recapi.rb https://api.<your-domain>/sdk/ruby/recapi-0.1.10.rb
34
34
  ```
35
35
 
36
36
  ## Quick start
@@ -74,7 +74,7 @@ and a log line.
74
74
  |---|---|
75
75
  | `track(item_id, level, user_id:, anon_id:, recommendation_id:)` | Records an interaction: `level` -10 to 10 or `:view` (3), `:cart` (5), `:purchase` (10), `:reject` (-10: never recommend the item to this user again until a positive event on it). The history changes at once; a background thread sends the event within 2 s (at once when a batch of 1,000 is full). An `item_id` the API would refuse (1-256 bytes of UTF-8, no control character; also `nil`, an Array or a Hash) drops the event with a log line: nothing raises, and the other events are sent as usual. A bad `level`, `user_id`, `anon_id` or `recommendation_id` still raises `ArgumentError`. |
76
76
  | `flush` | Sends the queued events now, with retries (end of a job). A network error, 429 or 5xx is retried 3 times (0.5 s, 1 s, 2 s with jitter, or the `Retry-After`, at most 8 s), then the events stay queued and the sender backs off up to a minute; any other error drops them (logged). |
77
- | `close` | Stops the sender and sends what is queued, one try per batch. It runs by itself at exit. |
77
+ | `close` | Stops the sender and sends what is queued, one try per batch. It runs by itself at exit, also for a classic Sinatra app (`ruby app.rb`), whose server runs from its own exit hook. Don't `track` while such an app loads, before its server starts: the client then closes before the first request, and every later `track` is sent on the page's thread. |
78
78
  | `recommend(user_id:, anon_id:, count:, placement:, tags_any:, tags_all:, exclude:, context_item_id:, discovery:)` | A `Recapi::Recommendations` (`recommendation_id`, `fallback?`, `items`). At most 1.5 s; on a network error, timeout, 5xx or 429 it returns popular items (`fallback?`, no id), or no item when those fail too: hide the slot; on a 401 or 403 no item at once. A `context_item_id` the API would refuse (also an Array or a Hash, as `params[:id]` gives for `?id[]=a`) isn't sent: you get the same popular list and a log line, with no recommendation request. Any other failure (a bad request, a reply that isn't the API's) raises. |
79
79
  | `identify(anon_id, user_id)` | At login: the visitor's history and shown items become the user's. The user's first restore from recapi then leaves that visitor's browsing out, so it doesn't count twice. |
80
80
  | `popular(count:, tags_any:, tags_all:)` | Popular items, for pages without a user. On a network error, timeout, 5xx, 429, 401 or 403: an empty list and a log line. Other failures as in `recommend`. |
@@ -122,6 +122,15 @@ and logs one line at start:
122
122
  so a visitor who never returns stays on disk (about 8 KB each): run
123
123
  `Rails.cache.cleanup` on a schedule, or leave `store:` out.
124
124
 
125
+ ## Changes
126
+
127
+ - 2026-11-05: version 0.1.10. `track` stays queued and in the background in a
128
+ classic Sinatra app
129
+ (`ruby app.rb`). The exit hook used to close the client before the server
130
+ started, so every event was sent on the page's thread, and a hung API
131
+ stalled each page for 5 s. The hook now starts with the sender, at the first
132
+ `track`.
133
+
125
134
  ## Development
126
135
 
127
136
  `../../scripts/ruby.sh -w test/run.rb [filter]` runs the tests (`make
data/lib/recapi.rb CHANGED
@@ -25,7 +25,7 @@ rescue LoadError
25
25
  end
26
26
 
27
27
  module Recapi
28
- VERSION = "0.1.9"
28
+ VERSION = "0.1.10"
29
29
 
30
30
  # Decayed user history, exactly as docs/history-spec.md (times in unix ms).
31
31
  # A port of sdk/python's history.py, snapshot merging included.
@@ -433,8 +433,8 @@ module Recapi
433
433
  TIMEOUT_ERRORS = [Net::OpenTimeout, Net::ReadTimeout, Net::WriteTimeout].freeze
434
434
  # A connection the server closed while idle fails this way, before any reply.
435
435
  STALE_ERRORS = [EOFError, Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED].freeze
436
- # Made here, not in initialize, whose block would hold the client through
437
- # self: the weak reference lets a dropped client be collected.
436
+ # Made here, not in enqueue, whose block would hold the client through self:
437
+ # the weak reference lets a dropped client be collected.
438
438
  CLOSE_AT_EXIT = lambda do |ref|
439
439
  at_exit do
440
440
  ref.__getobj__.close
@@ -480,7 +480,6 @@ module Recapi
480
480
  @sleeper = method(:wait)
481
481
  @random = -> { rand }
482
482
  init_threading
483
- CLOSE_AT_EXIT.call(WeakRef.new(self))
484
483
  end
485
484
 
486
485
  # Records an interaction: the user's history changes at once, the event
@@ -817,6 +816,12 @@ module Recapi
817
816
  @thread = Thread.new { run }
818
817
  @thread.name = "recapi-sender"
819
818
  @thread.report_on_exception = false
819
+ # Here, not in initialize: Sinatra's classic mode serves from its own
820
+ # at_exit, and exit hooks run last registered first, so a hook from
821
+ # the app's load would close the client before the first request.
822
+ # One registered while that server runs goes after the server ends.
823
+ # A forked worker starts its own sender, so it gets its own hook too.
824
+ CLOSE_AT_EXIT.call(WeakRef.new(self))
820
825
  end
821
826
  @queue << event
822
827
  drop_overflow if @queue.size > MAX_QUEUE
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - recapi