samvera / noid-rails Goto Github PK
View Code? Open in Web Editor NEWNoid identifier services for Rails based applications
License: Other
Noid identifier services for Rails based applications
License: Other
This follows the proposed maintenance reorganization within samvera/maintenance#137
This repository’s default branch has already been renamed using GitHub’s renaming tool. Links that reference the old branch name are automatically forwarded to the new default branch. But string references are not automatically updated.
Check this repository for hard-coded string references to the legacy “master” default branch and update them to the new default branch name “main.”
Important places to check include, but are not limited to:
NOTE: READMEs, wikis, and other documentation are important to update to avoid confusion and correct errors in long lasting documentation.
Less common places to check:
NOTE: String references to themaster
branch in Issues, PRs, and code are uncommon. Also Issues and PRs are temporal in nature, making it less critical to update those occurrences.
Git's default "master" branch derives from "master/slave" jargon which perpetuates systemic racist language and systems (see email Replacing "master" reference in git branch names). To uphold our Code of Conduct, we must move away from the term "master" in our technical language (as well as words like blacklist or whitelist).
The database-backed minter (db
branch) is coming along well, but there are some design questions that need broader vetting.
LOCKING: Obviously we will use a DB transaction, but the transaction only helps for our connection. The point of the DB for horizontal scalability is that potentially many connections will be concurrent. What kind of locking do we want to use?
ActiveRecord::StaleObjectError
LOCK IN SHARE MODE
) or other?Pick your poison.
FAILSAFE: The point of locking is that one way or another only one connection/thread writes to the DB at a time. That means by design somebody loses (at write time for optimistic, at read time for pessimistic). The mint
method is designed to loop forever until it gets a novel NOID, so presumably catching the failure there and retrying is OK, but at a certain scale, after a certain amount of failure, cycling forever in race/deadlock is counter-productive.
So, based on the lock you prefer, what tolerance for failure do we have here? Raise after 100 StaleObjectError
or timeout after X to avoid deadlock?
Add the following branch renaming language to the README for this repository.
## Contributing
If you're working on PR for this project, create a feature branch off of `main`.
This repository follows the [Samvera Community Code of Conduct](https://samvera.atlassian.net/wiki/spaces/samvera/pages/405212316/Code+of+Conduct) and [language recommendations](https://github.com/samvera/maintenance/blob/master/templates/CONTRIBUTING.md#language). Please ***do not*** create a branch called `master` for this repository or as part of your pull request; the branch will either need to be removed or renamed before it can be considered for inclusion in the code base and history of this repository.
Git's default "master" branch derives from "master/slave" jargon which perpetuates systemic racist language and systems (see email Replacing "master" reference in git branch names). To uphold our Code of Conduct, we must move away from the term "master" in our technical language (as well as words like blacklist or whitelist).
Description
The identifier_in_use attribute can be changed by the identifier_in_use attr_writer, but it will not stay changed. Each time you call the reader it is changed back to the lambda that always returns false
.
Expected
def identifier_in_use
@identifier_in_use ||= lambda do |_id|
false
end
end
Actual
def identifier_in_use
@identifier_in_use = lambda do |_id|
false
end
end
https://github.com/samvera/noid-rails/blob/master/lib/noid/rails/config.rb#L29
This is something I hadn't looked closely enough at before pushing the PR previously. If you override id generation, Fedora will properly be handed the desired path - for instance, be/19/d0/b2/2514nz446
. If you do not override id generation and let Fedora do its UUID thing, Fedora chooses the path - for instance, 4a/48/49/6a/4a48496a-d56b-44a0-9836-e3381dfe13bd
.
With UUIDs, the bucketing system is able to use the first 8 characters because they are never predictable. With NOIDs, there is a predictability, which is why I pushed up #3 to begin with.
The problem here can originate if you do not specify IDs prior to saving in Fedora for every single object you ever create. If there is any situation where an object is created in Fedora without both a predetermined PID and the overridden path translation code, things will be essentially unfindable by the app.
For most users this probably isn't an issue, but it's the sort of problem that could hit unexpectedly and won't make a lot of sense if it does.
I think the best long-term solution would be to let Fedora bucket by UUID and then have our pids be simply a piece of data associated with the object, rather than the pids being the definitive ID. I have no idea how to make this happen, as I (obviously) am not all that familiar with the Hydra stack, especially Fedora 4 / Hydra 9.
Barring that, it might be good to have the treeifier just return the id as-is unless it's a valid noid. Then anything not minted as a noid could just use the built-in fedora treeification. This is sort of weird and magicky, but it might be a good fix for the vast majority of use cases (e.g., creating a dummy asset in a test where the minting logic is deliberately not wanted).
This repository’s default branch has already been renamed to main
using GitHub’s renaming tool. In order to preserve automatic redirection of links that reference the old branch name master
to the new default main
branch, a branch with the old name should not be recreated.
CircleCI can be used to prevent the recreation of the old default branch name by preventing PRs with a branch named master
from being merged by causing a test failure during continuous integration.
Git's default "master" branch derives from "master/slave" jargon which perpetuates systemic racist language and systems (see email Replacing "master" reference in git branch names). To uphold our Code of Conduct, we must move away from the term "master" in our technical language (as well as words like blacklist or whitelist).
If a PR is submitted with a branch named master, the continuous integration tests should fail.
If a PR is submitted with a branch named master, the continuous integration tests will not fail because of the branch name.
Background on the renaming effort is available in the working group notes.
Derived from samvera/maintenance#89
Currently:
uri = http://localhost:8983/fedora/rest/test/hh/63/vz/22/hh63vz22q/members"
p ActiveFedora::Base.uri_to_id(uri)
"members"
it should be
"hh63vz22q/members"
You do not have permission to push to this gem.
This gem should probably be added to https://github.com/projecthydra/hydra/blob/master/script/grant_revoke_gem_authority.rb and then @mjgiarlo (the only person with access) can run it.
"http://localhost:8983/fedora/rest/test/te/st/5/test5/files"
(byebug) p ActiveFedora::Base.uri_to_id(uri)
"files"
I would expect test5/files
instead
getting the wrong id for short ids.
This is derived from samvera/maintenance#119, and follows the guide in https://guides.rubygems.org/mfa-requirement-opt-in/
ActiveFedora::Noid.config.translate_uri_to_id.call(RDF::URI('http://127.0.0.1:8986/rest/test/sf/26/85/66/sf2685667/list_source#g70294565559000')
=> "sf2685667/list_source"
But the default ActiveFedora one doesn't:
ActiveFedora::Core::FedoraUriTranslator.call(RDF::URI('http://127.0.0.1:8984/rest/dev/list_source#g70294565559000'))
=> "list_source#g70294565559000"
The default minter has been the file-based SynchronizedMinter. With the major version bump and availability of DB-backed minter, do we want to shift the default to that?
Documentation under the "ActiveFedora" integration is missing information about samvera-deprecated/sufia@21fe2ac#diff-f40d980889b56e5c9b6b53f6abead3feR69 .
This code is in production currently, yes?
Ruby 2.7.0 was released on 12/25/2019, and in accordance with the charter of the current phase of the Component Maintenance Working Group, the CircleCI configuration for this should be updated.
Derived from samvera/maintenance#76
The Renaming Branch Working Group is in the process of renaming the default branch from master
to main
in Samvera and Samvera-Labs repos. This brings repositories into compliance with the Samvera Community Code of Conduct (https://samvera.atlassian.net/wiki/spaces/samvera/pages/405212316/Code+of+Conduct) and language recommendations (https://github.com/samvera/maintenance/blob/master/templates/CONTRIBUTING.md#language).
This issue will be complete when the master
has been renamed to main
.
Related issues will have a title beginning with RENAME
.
Pulled from my retrospective comment on #40.
I think it is problematic to have extracted locking to outside the transaction. Locking for update now happens in multiple places, including on every read
. Previously only a transaction could block another transaction. Now a read
can.
read
and write!
can now block each other: each calls instance
which calls MinterState.lock.find_by
, so they are not working on the same object, but each has a lock.
read
and write!
being public (and superficially documented) methods is sorta a problem. I mean, the Base class says read
returns a Hash representing the minter state. Both it and the concrete class should be able to be more specific. (E.G., are the keys symbolic, string or mixed? what's this magic JSON marshalling?) Also, since it is public, a consumer might reasonably call read
and be unaware that a locking operation is the result.
By my interpretation, read
does not need SELECT ... FOR UPDATE
for an instance it converts to a Hash, then throws away. Unlike before, no update ever comes for that object! The point of having the transaction was to put the read and the save!
on the same object (as previously the case).
Makes production deploys fail.
Because consumer may not be using rails. (if defined? Rails
or similar)
def default_minter
@minter ||= ActiveFedora::Noid::SynchronizedMinter.new
end
This means the instance variable is set twice during initialization and that the default_minter
method has an unnecessary side-effect.
Rails version 6.0.0 was released on 08/16/2019, and in accordance with the charter of the current phase of the Component Maintenance Working Group, the CircleCI configuration for this should be updated.
Derived from samvera/maintenance#17
Migrating an object with a 7 digit id was yielding empty path segments in the members URI when CC was configured with the default (noid) id-uri translators. Reverting to AF basic fixed the problem.
Example: 1667751 -> /dev/16/67/75/1//1667751/members
NoMethodError: private method `select' called for nil:NilClass
[GEM_ROOT]/gems/active_fedora-noid-2.0.0/lib/active_fedora/noid/minter/db.rb:9 :in `read`
7 class Db < Base
8 def read
9 filtered_hash = instance.as_json.select { |key| %w(template counters seq rand namespace).include?(key) }
10 filtered_hash['counters'] = JSON.parse(filtered_hash['counters'], symbolize_names: true) if filtered_hash['counters']
11 filtered_hash.symbolize_keys
[GEM_ROOT]/gems/active_fedora-noid-2.0.0/lib/active_fedora/noid/minter/db.rb:38 :in `block in next_id`
[GEM_ROOT]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:232 :in `block in transaction`
[GEM_ROOT]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/transaction.rb:189 :in `within_new_transaction`
[GEM_ROOT]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:232 :in `transaction`
[GEM_ROOT]/gems/activerecord-5.0.1/lib/active_record/transactions.rb:211 :in `transaction`
[GEM_ROOT]/gems/active_fedora-noid-2.0.0/lib/active_fedora/noid/minter/db.rb:37 :in `next_id`
35 def next_id
36 id = nil
37 MinterState.transaction do
38 state = read
39 minter = ::Noid::Minter.new(state)
[GEM_ROOT]/gems/active_fedora-noid-2.0.0/lib/active_fedora/noid/minter/base.rb:25 :in `block (2 levels) in mint`
[GEM_ROOT]/gems/active_fedora-noid-2.0.0/lib/active_fedora/noid/minter/base.rb:24 :in `loop`
[GEM_ROOT]/gems/active_fedora-noid-2.0.0/lib/active_fedora/noid/minter/base.rb:24 :in `block in mint`
[GEM_ROOT]/gems/active_fedora-noid-2.0.0/lib/active_fedora/noid/minter/base.rb:23 :in `synchronize`
[GEM_ROOT]/gems/active_fedora-noid-2.0.0/lib/active_fedora/noid/minter/base.rb:23 :in `mint`
[GEM_ROOT]/gems/active_fedora-noid-2.0.0/lib/active_fedora/noid/service.rb:15 :in `mint`
[GEM_ROOT]/bundler/gems/hyrax-d247dd973fb6/app/services/hyrax/noid.rb:10 :in `assign_id`
[GEM_ROOT]/gems/active-fedora-11.0.1/lib/active_fedora/persistence.rb:206 :in `assign_rdf_subject`
[GEM_ROOT]/gems/active-fedora-11.0.1/lib/active_fedora/persistence.rb:165 :in `_create_record`
[GEM_ROOT]/gems/active-fedora-11.0.1/lib/active_fedora/indexing.rb:63 :in `_create_record`
[GEM_ROOT]/gems/active-fedora-11.0.1/lib/active_fedora/callbacks.rb:240 :in `block in _create_record`
[GEM_ROOT]/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:126 :in `call`
[GEM_ROOT]/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:506 :in `block (2 levels) in compile`
[GEM_ROOT]/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:455 :in `call`
[GEM_ROOT]/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:101 :in `__run_callbacks__`
[GEM_ROOT]/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:750 :in `_run_create_callbacks`
[GEM_ROOT]/gems/active-fedora-11.0.1/lib/active_fedora/callbacks.rb:240 :in `_create_record`
[GEM_ROOT]/gems/active-fedora-11.0.1/lib/active_fedora/persistence.rb:159 :in `create_or_update`
[GEM_ROOT]/gems/active-fedora-11.0.1/lib/active_fedora/callbacks.rb:236 :in `block in create_or_update`
[GEM_ROOT]/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:126 :in `call`
[GEM_ROOT]/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:506 :in `block (2 levels) in compile`
[GEM_ROOT]/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:455 :in `call`
[GEM_ROOT]/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:101 :in `__run_callbacks__`
[GEM_ROOT]/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:750 :in `_run_save_callbacks`
[GEM_ROOT]/gems/active-fedora-11.0.1/lib/active_fedora/callbacks.rb:236 :in `create_or_update`
[GEM_ROOT]/gems/active-fedora-11.0.1/lib/active_fedora/persistence.rb:29 :in `save`
[GEM_ROOT]/gems/active-fedora-11.0.1/lib/active_fedora/validations.rb:50 :in `save`
[GEM_ROOT]/bundler/gems/hyrax-d247dd973fb6/app/services/hyrax/admin_set_create_service.rb:19 :in `create`
17 admin_set.edit_groups = ['admin']
18 admin_set.creator = [creating_user.user_key]
19 admin_set.save.tap do |result|
20 create_permission_template if result
21 end
Running rails generate active_fedora:noid:install
results in the following error:
[WARNING] Could not load generator "generators/active_fedora/noid/install_generator". Error: undefined method `expand_path' for ActiveFedora::File:Class.
/Users/dixonj16/.rvm/gems/ruby-2.3.0/gems/active_fedora-noid-2.0.0.beta1/lib/generators/active_fedora/noid/install_generator.rb:4:in `<class:InstallGenerator>'
/Users/dixonj16/.rvm/gems/ruby-2.3.0/gems/active_fedora-noid-2.0.0.beta1/lib/generators/active_fedora/noid/install_generator.rb:3:in `<module:Noid>'
This can be reproduced by creating a new rails app and adding the following gems.
gem 'active_fedora-noid', '2.0.0.beta1'
gem 'active-fedora', '10.1.0'
Derived from samvera/maintenance#77
$ rails g active_fedora:noid:install
info Installing ActiveFedora::Noid
rake active_fedora_noid_engine:install:migrations
rake aborted!
Don't know how to build task 'active_fedora_noid_engine:install:migrations' (see --tasks)
(See full trace by running task with --trace)
rake db:migrate
generate active_fedora:noid:seed
info Initializing database table for namespace:template of 'default:.reeddeeddk'
/Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/active_fedora-noid-2.0.0.beta3/lib/generators/active_fedora/noid/seed_generator.rb:22:in `seed_row': uninitialized constant ActiveFedora::Noid::SeedGenerator::MinterState (NameError)
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/thor-0.19.1/lib/thor/command.rb:27:in `run'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/thor-0.19.1/lib/thor/invocation.rb:126:in `invoke_command'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/thor-0.19.1/lib/thor/invocation.rb:133:in `block in invoke_all'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/thor-0.19.1/lib/thor/invocation.rb:133:in `each'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/thor-0.19.1/lib/thor/invocation.rb:133:in `map'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/thor-0.19.1/lib/thor/invocation.rb:133:in `invoke_all'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/thor-0.19.1/lib/thor/group.rb:232:in `dispatch'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/thor-0.19.1/lib/thor/base.rb:440:in `start'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.0.1/lib/rails/generators.rb:180:in `invoke'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.0.1/lib/rails/commands/generate.rb:13:in `<top (required)>'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.0.1/lib/rails/commands/commands_tasks.rb:138:in `require_command!'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.0.1/lib/rails/commands/commands_tasks.rb:145:in `generate_or_destroy'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.0.1/lib/rails/commands/commands_tasks.rb:60:in `generate'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.0.1/lib/rails/commands/commands_tasks.rb:49:in `run_command!'
from /Users/jcoyne/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.0.1/lib/rails/commands.rb:18:in `<top (required)>'
from bin/rails:4:in `require'
from bin/rails:4:in `<main>'
class LocalNames
include Blacklight::SearchHelper
include Blacklight::Configurable
copy_blacklight_config_from CatalogController
blacklight_config do |conf|
# conf.search_builder_class = LocalNameSearchBuilder
config.default_solr_params = {
'qf' => 'foaf_name_tesim',
}
end
def initialize
puts blacklight_config.default_solr_params
end
end
LocalNames.new
This prints out the CatalogController defaults, not the defaults I set in this class. Is that right?
Derived from samvera/maintenance#16
The call to instance
locks the object, yet we never update or free it.
The maintenance template for CONTRIBUTING.md is used by all Samvera repos. Replace the current version with the updated maintenance template version.
See discussion in #5.
Reliance on filesystem for statefile prevents horizontal scaling of the application (as we need in HyBox). Reliance on single path in filesystem also blocks multitenancy. The solution for either of these is to use a db-backed "statefile".
Design questions
Should this be a separate class here akin to SynchronizedMinter
? a fork of this project? a totally independent gem?
For performance, ideally the minter is a stored procedure in the db-layer, to minimize the exclusive lock duration. Potentially a second implementation might be required in sqlite3 for development (still useless for the production cases mentioned above). It might need separate code for mysql and postgres. But introduction of specific db-adapter dependencies in AF:noid
seems undesirable, or at least a marked shift in the profile of the gem.
If created at a rails engine level (Sufia, CurationConcerns, etc.), the dependency profile there doesn't change (as much), but the reusability of the minter is limited.
I am leaning towards a separate gem, possibly a separate gem per DB adapter. I'd welcome some feedback from folks w/ more experience around current noid generation.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.