Skip to content

Commit

Permalink
sqale: Make ExtItemCache cluster safe
Browse files Browse the repository at this point in the history
If insert fails because of unique constraint, lookup DB to retrieve IDs
  • Loading branch information
tonydamage committed Sep 14, 2021
1 parent 795eefe commit 6221cd7
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 12 deletions.
Expand Up @@ -91,12 +91,34 @@ private void updateMaps(MExtItem row) {

extItem = MExtItem.of(id, extItemKey);
updateMaps(extItem);
} catch (RuntimeException e) {
if (SqaleUtils.isUniqueConstraintViolation(e)) {
extItem = retrieveFromDb(extItemKey);
} else {
throw e;
}
}

LOGGER.debug("Ext item cache row inserted: {}", extItem);
return extItem;
}

private MExtItem retrieveFromDb(@NotNull MExtItem.Key key) {
QExtItem ei = QExtItem.DEFAULT;
MExtItem row = jdbcSessionSupplier.get().newQuery()
.select(ei)
.from(ei)
.where(ei.itemName.eq(key.itemName))
.where(ei.valueType.eq(key.valueType))
.where(ei.holderType.eq(key.holderType))
.where(ei.cardinality.eq(key.cardinality))
.fetchOne();
if (row != null) {
updateMaps(row);
}
return row;
}

public @Nullable MExtItem getExtensionItem(@NotNull MExtItem.ItemNameKey extItemKey) {
if (jdbcSessionSupplier == null) {
throw new IllegalStateException("Ext item cache was not initialized yet!");
Expand Down
Expand Up @@ -119,6 +119,11 @@ public static void handlePostgresException(Exception exception)
}
}

public static boolean isUniqueConstraintViolation(Exception exception) {
PSQLException psqlException = ExceptionUtil.findCause(exception, PSQLException.class);
return PSQLState.UNIQUE_VIOLATION.getState().equals(psqlException.getSQLState());
}

public static String toString(Object object) {
return new ToStringUtil(object).toString();
}
Expand Down
Expand Up @@ -22,17 +22,16 @@
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.querydsl.core.QueryException;

/**
* Component hiding details of how QNames are stored in {@link QUri}.
* Following prefixes are used for its methods:
*
* * `get` returns URI or ID for ID or URI, may return null, no DB access; TODO: later possible access when not found
* * `get` returns URI or ID for ID or URI, may return null, no DB access;
* * `search` like `get` but returns {@link #UNKNOWN_ID} instead of null, used for query predicates,
* no DB access by the URI cache itself; TODO: later possible access when not found
* no DB access by the URI cache itself;
* * `resolve` returns URI/ID for ID/URI or throws exception if not found, this is for situations
* where the entry for URI is expected to exist already, still no DB access required; TODO: later possible access when not found
* where the entry for URI is expected to exist already, still no DB access required;
* * finally, {@link #processCacheableUri(Object)} accesses the database if the URI is not found
* in the cache in order to write it there.
*
Expand Down Expand Up @@ -208,16 +207,18 @@ private Integer retrieveId(String uri) {
jdbcSession.commit();

updateMaps(MUri.of(id, uriString));
} catch (QueryException e) {
// Insert failed, record probably exists, so lets try to retrieve it
Integer retId = retrieveIdFromDb(uriString);
if (retId == null) {
throw new IllegalStateException("Couldn't insert uri to cache and uri was not present in cache.", e);
} catch (RuntimeException e) {

if (SqaleUtils.isUniqueConstraintViolation(e)) {
// Insert failed, record exists, so lets try to retrieve it
Integer retId = retrieveIdFromDb(uriString);
if (retId == null) {
throw new IllegalStateException("Couldn't insert uri to cache and uri was not present in cache.", e);
}
return retId;
}
return retId;
throw e;
}
// TODO query when constraint violation

LOGGER.debug("URI cache inserted URI={} under ID={}", uri, id);
return id;
}
Expand Down

0 comments on commit 6221cd7

Please sign in to comment.