Skip to content

Never call getAndTouch in ReactiveFindByIdOperationSupport #1634

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jorgerod opened this issue Jan 5, 2023 · 5 comments · Fixed by #1640 or #1641
Closed

Never call getAndTouch in ReactiveFindByIdOperationSupport #1634

jorgerod opened this issue Jan 5, 2023 · 5 comments · Fixed by #1640 or #1641
Assignees
Labels
status: duplicate A duplicate of another issue

Comments

@jorgerod
Copy link
Contributor

jorgerod commented Jan 5, 2023

Hello

I'm trying to do a getAndTouch in the

public Mono<T> one(final Object id) {
CommonOptions<?> gOptions = initGetOptions();

method and I think there is a bug.
The options are never initialized so the get method is always executed.

Related issue: #1196

Thank you very much

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jan 5, 2023
@mikereiche mikereiche added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged labels Jan 5, 2023
@mikereiche
Copy link
Collaborator

mikereiche commented Jan 5, 2023

Can you show the call you are making?

This is the test case for getAndTouch() being used when an expiry is specified.

@mikereiche mikereiche self-assigned this Jan 9, 2023
@mikereiche mikereiche added status: duplicate A duplicate of another issue and removed status: waiting-for-feedback We need additional information before we can continue labels Jan 9, 2023
@jorgerod
Copy link
Contributor Author

jorgerod commented Jan 10, 2023

Hi @mikereiche

Sorry I couldn't answer you before, but I've been busy.

As I understand it, to do getAndTouch you have to take into account touchOnRead property defined in @Document

According to the javadoc of the touchOnRead property in org.springframework.data.couchbase.core.mapping.Document:

An optional flag associated with expiry() indicating whether the expiry timer should be reset whenever the document is directly read (eg. findByOne, findById).

And Spring Data Couchbase is not taking this property into account.
Is this the right behaviour? So, property touchOnRead is useless?

In fact, reviewing Spring Data Couchbase 3x, this property is taken into account (spring-data-couchbase 3.2.9-RELEASE. (Couchbase.java): ):

  @Override
  public <T> T findById(final String id, Class<T> entityClass) {
    final CouchbasePersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(entityClass);
    RawJsonDocument result = execute(new BucketCallback<RawJsonDocument>() {
      @Override
      public RawJsonDocument doInBucket() {
        if (entity.isTouchOnRead()) {
          return client.getAndTouch(id, entity.getExpiry(), RawJsonDocument.class);
        } else {
          return client.get(id, RawJsonDocument.class);
        }
      }
    });

    return mapToEntity(id, result, entityClass);
  }

Thank you very much

@mikereiche
Copy link
Collaborator

mikereiche commented Jan 10, 2023

Edit: it seems that relying solely on @expiry annotation would not work.

"options are never initialized"

It doesn't need to be initialized. from initGetOptions()

	if (expiry != null || options instanceof GetAndTouchOptions) {
		GetAndTouchOptions gOptions = options != null ? (GetAndTouchOptions) options : getAndTouchOptions();
		if (gOptions.build().transcoder() == null) {
			gOptions.transcoder(RawJsonTranscoder.INSTANCE);
		}
		getOptions = gOptions;

We can go back and forth with you saying that it doesn't work, and me pointing to the test that shows that it does work.
Do you have any new information? Can you provide an example of it not working?

The value comes from expiryToUse() in ReactiveFindByIdOperationSupport.

return rc.getAndTouch(id.toString(), expiryToUse(), (GetAndTouchOptions) pArgs.getOptions())
		.flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), domainType,
				pArgs.getScope(), pArgs.getCollection(), null, null));
private Duration expiryToUse() {
	Duration expiryToUse = expiry; << from withExpiry()
	if (expiryToUse != null || options instanceof GetAndTouchOptions) {
		if (expiryToUse == null) { // GetAndTouchOptions without specifying expiry -> get expiry from annoation
			final CouchbasePersistentEntity<?> entity = template.getConverter().getMappingContext()
					.getRequiredPersistentEntity(domainType);
			expiryToUse = entity.getExpiryDuration();
		}
	}
	return expiryToUse;
}

@jorgerod
Copy link
Contributor Author

Sorry, maybe I didn't make myself clear.

I'm going with an example as you asked me. Let's suppose we have the following code:

package com.jorge.couchbase.reactive.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.repository.ReactiveCouchbaseRepository;
import org.springframework.data.couchbase.repository.config.EnableReactiveCouchbaseRepositories;
import org.springframework.stereotype.Repository;

@Repository
interface UserWithEnableTouchOnReadRepository extends ReactiveCouchbaseRepository<UserWithEnableTouchOnRead, String> {}

@Repository
interface UserWithDisableTouchOnReadRepository extends ReactiveCouchbaseRepository<UserWithDisableTouchOnRead, String> {}

@Document(touchOnRead = true, expiry = 1000)
class UserWithEnableTouchOnRead  {

	String id;
	String name;
	public UserWithEnableTouchOnRead(String id, String name) {
		this.id = id;
		this.name = name;
	}

}

@Document(touchOnRead = false, expiry = 1000)
class UserWithDisableTouchOnRead  {

	String id;
	String name;

	public UserWithDisableTouchOnRead(String id, String name) {
		this.id = id;
		this.name = name;
	}

}
@SpringBootApplication
@EnableReactiveCouchbaseRepositories
public class DemoApplication  implements ApplicationRunner {
	
	@Autowired
	public UserWithEnableTouchOnReadRepository userWithEnableTouchOnReadRepository;
	
	@Autowired
	public UserWithDisableTouchOnReadRepository userWithDisableTouchOnReadRepository;
			
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

	@Override
	public void run(ApplicationArguments args) throws Exception {
		UserWithEnableTouchOnRead userWithEnableTouchOnRead = new UserWithEnableTouchOnRead("1", "1");
		UserWithDisableTouchOnRead userWithDisableTouchOnRead = new UserWithDisableTouchOnRead("2", "2");
		
		// Executes getAndTouch method
		userWithEnableTouchOnReadRepository.findById(userWithEnableTouchOnRead.id);
		
		// It executes getAndTouch method and I think it should NOT execute it.
		userWithDisableTouchOnReadRepository.findById(userWithDisableTouchOnRead.id);	
	}
}

According to the javadoc, only UserWithEnableTouchOnRead document should be executed as getAndTouch.

Currently, the two documents would be consulted as getAndTouch.

@mikereiche How do you see him? With the current behaviour, what is the touchOnRead attribute of the @Document annotation used for?

Thank you very much for your answers and the time you are dedicating to it

@mikereiche
Copy link
Collaborator

mikereiche commented Jan 11, 2023

  // It executes getAndTouch method and I think it should NOT execute it.

Why should it not be called?

what is the touchOnRead attribute of the @document annotation used for?

Ignored. I was unaware of such an annotation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: duplicate A duplicate of another issue
Projects
None yet
3 participants