Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

spring data elasticsearch dynamic multi tenant index mismatch?

I am experimenting with spring data elasticsearch by implementing a cluster which will host multi-tenant indexes, one index per tenant.

I am able to create and set settings dynamically for each needed index, like

public class SpringDataES {

    @Autowired
    private ElasticsearchTemplate es;

    @Autowired
    private TenantIndexNamingService tenantIndexNamingService;

    private void createIndex(String indexName) {
        Settings indexSettings = Settings.builder()
                        .put("number_of_shards", 1)
                        .build();
        CreateIndexRequest indexRequest = new CreateIndexRequest(indexName, indexSettings);
        es.getClient().admin().indices().create(indexRequest).actionGet();
        es.refresh(indexName);
    }

    private void preapareIndex(String indexName){
        if (!es.indexExists(indexName)) {
            createIndex(indexName);
        }
        updateMappings(indexName);
    }

The model is created like this

@Document(indexName = "#{tenantIndexNamingService.getIndexName()}", type = "movies")
public class Movie {

  @Id
  @JsonIgnore
  private String id;
  private String movieTitle;
  @CompletionField(maxInputLength = 100)
  private Completion movieTitleSuggest;
  private String director;
  private Date releaseDate;

where the index name is passed dynamically via the SpEl

#{tenantIndexNamingService.getIndexName()}

that is served by

@Service
public class TenantIndexNamingService {
  private static final String INDEX_PREFIX = "test_index_";

  private String indexName = INDEX_PREFIX;

  public TenantIndexNamingService() {
  }

  public String getIndexName() {
    return  indexName;
  }

  public void setIndexName(int tenantId) {
    this.indexName = INDEX_PREFIX + tenantId;
  }

  public void setIndexName(String indexName) {
    this.indexName = indexName;
  }
}

So, whenever I have to execute a CRUD action, first I am pointing to the right index and then to execute the desired action

tenantIndexNamingService.setIndexName(tenantId);
movieService.save(new Movie("Dead Poets Society", getCompletion("Dead Poets Society"), "Peter Weir", new Date()));

My assumption is that the following dynamically index assignment, will not work correctly in a multi-threaded web application:

@Document(indexName = "#{tenantIndexNamingService.getIndexName()}"

This is because TenantIndexNamingService is singleton.

So my question is how achieve the right behavior in a thread save manner?

like image 515
ItsIlias Avatar asked Nov 08 '22 10:11

ItsIlias


1 Answers

I would probably go with an approach similar to the following one proposed for Cassandra:

  • https://dzone.com/articles/multi-tenant-cassandra-cluster-with-spring-data-ca

You can have a look at the related GitHub repository here:

  • https://github.com/gitaroktato/spring-boot-cassandra-multitenant-example

Now, since Elastic has differences in how you define a Document, you should mainly focus in defining a request-scoped bean that will encapsulate your tenant-id and bind it to your incoming requests.

like image 133
kmandalas Avatar answered Nov 15 '22 06:11

kmandalas