samvera / valkyrie

A Data Mapper library to enable multiple backends for storage of files and metadata in Samvera
Other
34 stars 23 forks source link

Clarify Equality #765

Open no-reply opened 4 years ago

no-reply commented 4 years ago

Equality is currently based on the default Dry::Struct implementation, which compares all ResourceClass.__attributes__ . This includes ::Valkyrie::Persistence::OptimisticLockToken, which should likely be excluded.

We may want to consider excluding other reserved attributes.

class ResourceWithLock < Valkyrie::Resource)
  enable_optimistic_locking
end

resource1 = ResourceWithLock(id: 'blah', Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK => Valkyrie::Persistence::OptimisticLockToken.deserialize('lock_token:test:1'))
resource2 = ResourceWithLock(id: 'blah', Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK => Valkyrie::Persistence::OptimisticLockToken.deserialize('lock_token:test:2'))

resource1 == resource2 # => false
no-reply commented 4 years ago

One approach to this issue is to move the optimistic lock token out of #attributes.

That probably constitutes a major release, and might involve a bit of work since there's some use of the Dry::Types, but my intuition is that it's the best approach.

escowles commented 4 years ago

It looks like both Memory and Fedora are using the current Time of the object's instantiation to create the lock tokens:

tpendragon commented 4 years ago

The other option is we just change equality to be something like AR, where if it's persisted it == if the two share an ID/class (thanks @jrochkind for digging that up)

no-reply commented 4 years ago

i'll express a preference for Dry::Struct-style equality. it seems so easy to do ID equality as a client that using #== for it seems wasteful. resource.id == other.id

for reference: include Dry::Equalizer(:id) ought to be all it takes to implement #== as AR-style ID equality; including the class check.

tpendragon commented 3 years ago

I'm having a hard time figuring out how we can move this forward. I think I need some use cases where I want two things to be equal to one another if they have different lock tokens.

no-reply commented 2 years ago

@tpendragon: if i recall, the motivating need here was Sets. for example, i think the behavior of the following code varies depending on otherwise irrelevant details of the adapters optimistic locking behavior:

class Permission < Valkyrie::Resource
  enable_optimistic_locking
  attribute :mode
  attribute :user
end

class ResourceWithPermissions < Valkyrie::Resource
  enable_optimistic_locking
  attribute :permissions, Valkyrie::Types::Set.of(Permission)
end

p = Permission.new(mode: :read, user: 'user_key')
p = Valkyrie.config.metadata_adapter.persister.save(resource: p)

resource = ResourceWithPermissions.new(permissions: [p])

p2 = Valkyrie.config.metadata_adapter.query_service.find_by(id: p.id)
resource.permissions << p2

a practical example exists in Hyrax: https://github.com/samvera/hyrax/blob/f8706bc91d1caa3a4cdd73401cc7d081ec158437/app/models/hyrax/permission.rb#L13

but honestly, i think it would be desirable as a matter of principle for Valkyrie.config.metadata_adapter.query.find_by(id: 'some_id') == Valkyrie.config.metadata_adapter.query.find_by(id: 'some_id') to be true for all adapters.