Skip to content

DRAFT, DO NOT MERGE 7450/loan availability updater#13067

Draft
benbdeitch wants to merge 10 commits into
internetarchive:masterfrom
benbdeitch:7450/loan-availability-updater
Draft

DRAFT, DO NOT MERGE 7450/loan availability updater#13067
benbdeitch wants to merge 10 commits into
internetarchive:masterfrom
benbdeitch:7450/loan-availability-updater

Conversation

@benbdeitch

Copy link
Copy Markdown
Collaborator

Closes #7450

When completed, it will add a Solr field for ebook lending availability, with an updater that runs at set intervals. This avoids the current issue of every request having to be sent through to the IA endpointt.

Technical

Testing

Screenshot

Stakeholders

mekarpeles and others added 9 commits June 21, 2026 13:04
…7450)

Adds a loan_availability_updater script that polls IA's loan changes API
and atomically updates two new Solr fields on work documents:

- ebook_availability: "available" | "unavailable"
- ebook_becomes_available: ISO-8601 UTC loan expiry timestamp

On startup (or after a full Solr re-index), the script binary-searches
for the uid approximately 14 days ago so that all currently-active loans
are reflected without replaying the entire history. Once caught up it
polls every 30s; a full batch (1000 rows) is processed without sleeping.
Expired loans are evicted each cycle via a Solr range query on
ebook_becomes_available, so missed return/expire events self-heal.

Files changed:
- openlibrary/core/lending.py: add get_loan_changes() S3 GET helper
- conf/solr/conf/managed-schema.xml: add ebook_availability / ebook_becomes_available fields
- openlibrary/solr/solr_types.py: type-stub the two new fields
- scripts/solr_updater/loan_availability_updater.py: main polling script
- scripts/solr_updater/tests/test_loan_availability_updater.py: 20 unit tests

Closes internetarchive#7450
Fixes the python_tests CI failure: solr_types.py was manually edited
with a Literal type and inline comment that the auto-generator doesn't
produce; regenerate to match (string → Optional[str]).

Cleanup pass:
- Remove 35-line module docstring → 8 lines
- Remove section-banner comments throughout
- Remove BINARY_SEARCH_ITERS module constant → inline range(40)
- Remove redundant local type annotations
- Collapse new_uid/last_uid into a single variable computed once
- Use contextlib.suppress (ruff SIM105)
- DRY resolve_work_keys into a dict comprehension
- Trim all function docstrings to one paragraph
- Remove unused _make_loan_changes_response test helper
- Fix test call-count assertion to match inline range(40)
- Add lending.setup(infogami.config) in main() so config globals are populated
- Wrap find_start_uid() API calls in try/except for network resilience
- Quote identifiers in resolve_work_keys() Solr query (ia:("a" "b" "c"))
- Only set ebook_becomes_available for active loans with a parseable until value
- Move write_state() to after successful Solr commit to keep state consistent
- Add resolve_work_keys unit tests (empty input + basic mapping + query quoting)
Adds a loan_uid (plong) field to the Solr schema. The loan_availability_updater
writes the processed event uid to every work it updates, so that on restart
(or after a full re-index that wipes the field) the updater can query
max(loan_uid) from Solr to resume where it left off — avoiding the need for
a manual --reset flag after a re-index.

Startup uid resolution order: state file → max(loan_uid) in Solr → binary search.

Also adds plong to the types_generator type_map so future plong fields
are handled automatically.
@benbdeitch benbdeitch requested a review from cdrini as a code owner June 30, 2026 16:59
@benbdeitch benbdeitch marked this pull request as draft June 30, 2026 16:59
@github-actions github-actions Bot added the Priority: 2 Important, as time permits. [managed] label Jun 30, 2026
Comment on lines +166 to +167
<field name="ebook_availability" type="string" multiValued="false" stored="false" docValues="true" />
<field name="ebook_becomes_available" type="pdate" multiValued="false" stored="false" docValues="true" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ebook_availability could be an enum int [0 / 1] (e.g. unavailable, available)
  • ebook_next_available -> translate to epoch time, cleanly an int

@mekarpeles

Copy link
Copy Markdown
Member

If we switch from ptime to epoch for the loan_expiry field, then we'll want to confirm timezone which I believe it utc

epoch = dt.replace(tzinfo=timezone.utc).timestamp()

# If dt is naive and represents local time:
epoch = dt.timestamp()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Priority: 2 Important, as time permits. [managed]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Book Availability in Solr

3 participants