mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
CE-938 Committed too early last time
This commit is contained in:
@ -209,6 +209,17 @@ public class QInstanceValidator
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void revalidate(QInstance qInstance) throws QInstanceValidationException
|
||||||
|
{
|
||||||
|
qInstance.setHasBeenValidated(null);
|
||||||
|
validate(qInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -63,7 +63,7 @@ public class ProcessLockMetaDataProducer implements MetaDataProducerInterface<Me
|
|||||||
.withRecordLabelFormat("%s %s")
|
.withRecordLabelFormat("%s %s")
|
||||||
.withRecordLabelFields("processLockTypeId", "key")
|
.withRecordLabelFields("processLockTypeId", "key")
|
||||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "processLockTypeId", "key")))
|
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "processLockTypeId", "key")))
|
||||||
.withSection(new QFieldSection("data", new QIcon().withName("text_snippet"), Tier.T2, List.of("userId", "sessionUUID", "message", "checkInTimestamp", "expiresAtTimestamp")))
|
.withSection(new QFieldSection("data", new QIcon().withName("text_snippet"), Tier.T2, List.of("userId", "sessionUUID", "details", "checkInTimestamp", "expiresAtTimestamp")))
|
||||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")))
|
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
package com.kingsrook.qqq.backend.core.processes.locks;
|
package com.kingsrook.qqq.backend.core.processes.locks;
|
||||||
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
@ -46,6 +45,7 @@ import com.kingsrook.qqq.backend.core.model.session.QSession;
|
|||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
|
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
|
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.memoization.Memoization;
|
import com.kingsrook.qqq.backend.core.utils.memoization.Memoization;
|
||||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
@ -108,12 +108,22 @@ public class ProcessLockUtils
|
|||||||
QRecord existingLockRecord = new GetAction().executeForRecord(new GetInput(ProcessLock.TABLE_NAME).withUniqueKey(Map.of("key", key, "processLockTypeId", lockType.getId())));
|
QRecord existingLockRecord = new GetAction().executeForRecord(new GetInput(ProcessLock.TABLE_NAME).withUniqueKey(Map.of("key", key, "processLockTypeId", lockType.getId())));
|
||||||
if(existingLockRecord != null)
|
if(existingLockRecord != null)
|
||||||
{
|
{
|
||||||
existingLockDetails.append("Held by: ").append(existingLockRecord.getValueString("holder"));
|
ProcessLock existingLock = new ProcessLock(existingLockRecord);
|
||||||
Instant expiresAtTimestamp = existingLockRecord.getValueInstant("expiresAtTimestamp");
|
if(StringUtils.hasContent(existingLock.getUserId()))
|
||||||
|
{
|
||||||
|
existingLockDetails.append("Held by: ").append(existingLock.getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(StringUtils.hasContent(existingLock.getDetails()))
|
||||||
|
{
|
||||||
|
existingLockDetails.append("; with details: ").append(existingLock.getDetails());
|
||||||
|
}
|
||||||
|
|
||||||
|
Instant expiresAtTimestamp = existingLock.getExpiresAtTimestamp();
|
||||||
if(expiresAtTimestamp != null)
|
if(expiresAtTimestamp != null)
|
||||||
{
|
{
|
||||||
ZonedDateTime zonedExpiresAt = expiresAtTimestamp.atZone(ValueUtils.getSessionOrInstanceZoneId());
|
ZonedDateTime zonedExpiresAt = expiresAtTimestamp.atZone(ValueUtils.getSessionOrInstanceZoneId());
|
||||||
existingLockDetails.append("; Expires at: ").append(QValueFormatter.formatDateTimeWithZone(zonedExpiresAt));
|
existingLockDetails.append("; expiring at: ").append(QValueFormatter.formatDateTimeWithZone(zonedExpiresAt));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(expiresAtTimestamp != null && expiresAtTimestamp.isBefore(now))
|
if(expiresAtTimestamp != null && expiresAtTimestamp.isBefore(now))
|
||||||
@ -121,10 +131,9 @@ public class ProcessLockUtils
|
|||||||
/////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
// if existing lock has expired, then we can delete it and try to insert again //
|
// if existing lock has expired, then we can delete it and try to insert again //
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
Serializable id = existingLockRecord.getValue("id");
|
LOG.info("Existing lock has expired - deleting it and trying again.", logPair("id", existingLock.getId()),
|
||||||
LOG.info("Existing lock has expired - deleting it and trying again.", logPair("id", id),
|
|
||||||
logPair("key", key), logPair("type", typeName), logPair("details", details), logPair("expiresAtTimestamp", expiresAtTimestamp));
|
logPair("key", key), logPair("type", typeName), logPair("details", details), logPair("expiresAtTimestamp", expiresAtTimestamp));
|
||||||
new DeleteAction().execute(new DeleteInput(ProcessLock.TABLE_NAME).withPrimaryKey(id));
|
new DeleteAction().execute(new DeleteInput(ProcessLock.TABLE_NAME).withPrimaryKey(existingLock.getId()));
|
||||||
insertOutputRecord = tryToInsert(processLock);
|
insertOutputRecord = tryToInsert(processLock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,6 +180,7 @@ public class ProcessLockUtils
|
|||||||
{
|
{
|
||||||
Instant giveUpTime = Instant.now().plus(maxWait);
|
Instant giveUpTime = Instant.now().plus(maxWait);
|
||||||
|
|
||||||
|
UnableToObtainProcessLockException lastCaughtUnableToObtainProcessLockException = null;
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -180,6 +190,7 @@ public class ProcessLockUtils
|
|||||||
}
|
}
|
||||||
catch(UnableToObtainProcessLockException e)
|
catch(UnableToObtainProcessLockException e)
|
||||||
{
|
{
|
||||||
|
lastCaughtUnableToObtainProcessLockException = e;
|
||||||
if(Instant.now().plus(sleepBetweenTries).isBefore(giveUpTime))
|
if(Instant.now().plus(sleepBetweenTries).isBefore(giveUpTime))
|
||||||
{
|
{
|
||||||
SleepUtils.sleep(sleepBetweenTries);
|
SleepUtils.sleep(sleepBetweenTries);
|
||||||
@ -191,7 +202,12 @@ public class ProcessLockUtils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw (new UnableToObtainProcessLockException("Unable to obtain process lock for key [" + key + "] in type [" + type + "] after [" + maxWait + "]"));
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// var can never be null with current code-path, but prefer defensiveness regardless. //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@SuppressWarnings("ConstantValue")
|
||||||
|
String suffix = lastCaughtUnableToObtainProcessLockException == null ? "" : ": " + lastCaughtUnableToObtainProcessLockException.getMessage();
|
||||||
|
throw (new UnableToObtainProcessLockException("Unable to obtain process lock for key [" + key + "] in type [" + type + "] after [" + maxWait + "]" + suffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ import com.kingsrook.qqq.backend.core.BaseTest;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerMultiOutput;
|
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerMultiOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
@ -68,6 +69,7 @@ class ProcessLockUtilsTest extends BaseTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
metaData.addSelfToInstance(qInstance);
|
metaData.addSelfToInstance(qInstance);
|
||||||
|
new QInstanceValidator().revalidate(qInstance);
|
||||||
|
|
||||||
new InsertAction().execute(new InsertInput(ProcessLockType.TABLE_NAME).withRecordEntities(List.of(
|
new InsertAction().execute(new InsertInput(ProcessLockType.TABLE_NAME).withRecordEntities(List.of(
|
||||||
new ProcessLockType()
|
new ProcessLockType()
|
||||||
@ -104,7 +106,10 @@ class ProcessLockUtilsTest extends BaseTest
|
|||||||
// make sure we can't create a second for the same key //
|
// make sure we can't create a second for the same key //
|
||||||
/////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
assertThatThrownBy(() -> ProcessLockUtils.create("1", "typeA", "you"))
|
assertThatThrownBy(() -> ProcessLockUtils.create("1", "typeA", "you"))
|
||||||
.isInstanceOf(UnableToObtainProcessLockException.class);
|
.isInstanceOf(UnableToObtainProcessLockException.class)
|
||||||
|
.hasMessageContaining("Held by: " + QContext.getQSession().getUser().getIdReference())
|
||||||
|
.hasMessageContaining("with details: me")
|
||||||
|
.hasMessageNotContaining("expiring at: 20");
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
// make sure we can create another for a different key //
|
// make sure we can create another for a different key //
|
||||||
@ -171,7 +176,10 @@ class ProcessLockUtilsTest extends BaseTest
|
|||||||
// make sure someone else fails, if they don't wait long enough //
|
// make sure someone else fails, if they don't wait long enough //
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
assertThatThrownBy(() -> ProcessLockUtils.create("1", "typeC", "you", Duration.of(1, ChronoUnit.SECONDS), Duration.of(3, ChronoUnit.SECONDS)))
|
assertThatThrownBy(() -> ProcessLockUtils.create("1", "typeC", "you", Duration.of(1, ChronoUnit.SECONDS), Duration.of(3, ChronoUnit.SECONDS)))
|
||||||
.isInstanceOf(UnableToObtainProcessLockException.class);
|
.isInstanceOf(UnableToObtainProcessLockException.class)
|
||||||
|
.hasMessageContaining("Held by: " + QContext.getQSession().getUser().getIdReference())
|
||||||
|
.hasMessageContaining("with details: me")
|
||||||
|
.hasMessageContaining("expiring at: 20");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user