Delete entity after specified time with JpaRepository - multithreading

I am using Spring Boot and H2 db. I have a Product entity and I want my application to be able to remove the product from the database, but my requirement is this: first set the active flag to false ( then this row will not be taken into account during fetching ) and after a specific period of time completely remove this row from db.
#Entity
#Table(name = "products")
public class Product {
#Id
#GeneratedValue(generator = "inc")
#GenericGenerator(name = "inc", strategy = "increment")
private int id;
private boolean active = true;
// getters & setters
}
And my method from the service layer responsible for setting the active flag to false and later complete deletion (I have nothing that does the second part of my requirement - complete deletion after specific period of time)
#Transactional
public void deleteProduct(int id) {
var target = repository.findProductById(id)
.orElseThrow(() -> new IllegalArgumentException("No product with given id"));
target.setActive(false);
// what should I add here to remove the target after a specific time?
}
EDIT
OK, I solved my problem:
#Transactional
public void deleteProduct(int id) {
var target = repository.findProductByIdAndActiveTrue(id)
.orElseThrow(() -> new IllegalArgumentException("No product with gicen id"));
target.setActive(false);
// complete removal after 150 seconds
new Thread(() -> {
try {
Thread.sleep(150000);
repository.deleteById(id);
} catch (Exception e) {
logger.error("Error removing the product");
}
}).start();
}
But now my question is if this is a safe solution as the user may start too many threads now so I think there is a better solution to my problem (safer in terms of multithreading)

I am not an expert but i think what you trying to achieve is bad practice.
I believe you should do a scheduling, for example ones per day.
You should update in db the value active. Set a schedule that will check the entries each time and if they have an active value of false then delete. Something like this:
public void deleteProduct(int id) {
//update value to false
repository.updateProductValue(id,false);
}
and your scheduling method:
#Scheduled(fixedRate = 150000)
public void deleteNonActiveProducts() {
List<Product> products = repository.findAllByFalseValue();
products.forEach(product -> repository.deleteById(product.getId());
}
With this what you are doing is that every 150000 milliseconds you repeat that task and each execution of this task is independent and non parallel.
Hope is useful to you.

Related

How can I set a custom field on Acumatica Production Detail Operation when the Production Order is first created?

I have a custom field on an Acumatica Production Detail Operation (UsrEligibleForRoboticFulfillment) that I have created an Action to set based on criteria on the component items in the Materials tab. (code below)
I would like to call this Action to set the field as soon as the Production Order is created, but the split nature of the Production Order is such that there are no events on the Production Detail raised that I can attach to and call the Action. I've tried Row Inserted as well as Persist delegate on the Production Detail graph.
I CAN attach to either the AMProdItem Row Inserted or Persist Delegate on the Production Maint graph, but at this point in time the Operations and Materials have not yet been created.
What's the best way to update this field when a new Production Order is created?
Action code:
public PXAction<AMProdItem> UpdateEligibleForRoboticFulfillment;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Update Robotic Eligibility", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
protected void updateEligibleForRoboticFulfillment()
{
AMProdItem prodDetail = Base.ProdItemRecords.Current;
AMProdOper prodOper = Base.ProdOperRecords.Current;
InventoryItem finishedProduct = PXSelect<InventoryItem,
Where<InventoryItem.inventoryID, Equal<Current<AMProdItem.inventoryID>>>>.Select(Base).FirstOrDefault();
//Only production orders are eligible for robotic fulfillment, not disassemblies
if (prodDetail.OrderType == "MO")
{
bool wasRoboticsEligible = (prodOper.GetExtension<AMProdOperExt>().UsrEligibleForRoboticFulfillment ?? false);
//Get the current branchID
int branchID = (int)Base.Accessinfo.BranchID;
//Get the default site/warehouse for this branch.
INSite site = INTranHelper.GetDefaultSiteForItemBranch(branchID);
//Get the flag indicating whether this site is active for robotics
bool activeRobotics = site.GetExtension<INSiteExt>().UsrActiveRobotics ?? false;
//Get the flags for manual process and component robotics compatible
bool requiresManualProcess = finishedProduct.GetExtension<InventoryItemExt>().UsrManualFinishRequired ?? false;
//Gotta be prepared for the possibility that more than one component is used
//Check for any components that are NOT robotics eligible that have qty required and haven't already been fully allocated
PXResultset<AMProdMatl> components = PXSelectJoin<AMProdMatl,
InnerJoin<InventoryItem, On<InventoryItem.inventoryID, Equal<AMProdMatl.inventoryID>,
And<InventoryItemExt.usrRoboticsCompatible, Equal<False>,
And<AMProdMatl.orderType, Equal<Current<AMProdOper.orderType>>,
And<AMProdMatl.prodOrdID, Equal<Current<AMProdOper.prodOrdID>>,
And<AMProdMatl.operationID, Equal<Current<AMProdOper.operationID>>,
And<AMProdMatl.qtyActual, Less<AMProdMatl.totalQtyRequired>,
And<AMProdMatl.qtyReq, Greater<decimal0>>>>>>>>>>
.Select(Base);
bool roboticsEligible = !requiresManualProcess && activeRobotics;
//If any component is not eligible, the whole operation is not eligible
if (components.Count > 0)
{
roboticsEligible = false;
}
//If the robotics eligible flag should have changed, change it
if (wasRoboticsEligible != roboticsEligible)
{
prodOper.GetExtension<AMProdOperExt>().UsrEligibleForRoboticFulfillment = roboticsEligible;
Base.ProdOperRecords.Update(prodOper);
}
}
}
Had to open a ticket with Acumatica; got a working solution! I had to enclose the persist delegate method in a transaction scope.
Override Persist() method of graph
Call base method first so that Operations and Materials on the Production Order Detail gets created
Enclosed in transaction scope
Something like this:
public delegate void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
if (/**/)
{
using (var ts = new PXTransactionScope())
{
//Call base method to persist
baseMethod();
/*Custom Logic here*/
ts.Complete();
}
}
else
baseMethod();
}

Release invoice on new screen

I need your help.
I have created a new screen, where I am calling all invoices pending release.
I have problems to release, I send a message where you request (you want to release).
It shows me the infinite message.
Only once should you ask me, then you should go out and follow the normal process.
public ProcessDocNew()
{
// Acuminator disable once PX1008 LongOperationDelegateSynchronousExecution [Justification]
Document.SetProcessDelegate(
delegate (List<ARInvoice> list)
{
List<ARRegister> newlist = new List<ARRegister>(list.Count);
foreach (ARInvoice doc in list)
{
newlist.Add(doc);
}
ProcessDoc(newlist, true);
}
);
Document.SetProcessCaption(ActionsMensje.Process);
Document.SetProcessAllCaption(ActionsMensje.ProcessAll);
}
public virtual void ProcessDoc(List<ARRegister> list, bool isMassProcess)
{
string title = "Test";
string sms = "¿Stamp?";
var Graph = PXGraph.CreateInstance<ARInvoiceEntry>();
ARInvoice document = Document.Current;
PEFEStampDocument timbrar = new PEFEStampDocument();/*This is a class where it is, all my method*/
if (isMassProcess == true)
{
Document.Ask(title, sms, MessageButtons.YesNo, MessageIcon.Question);
{
PXLongOperation.StartOperation(Graph, delegate
{
timbrar.Stamp(document, Graph); /*here I have my release method*/
});
}
}
}
public static class ActionsMensje
{
public const string Process = "Process";
public const string ProcessAll = "Process All";
}
I await your comments
Only once should you ask me, then you should go out and follow the
normal process.
That is not how the processing pattern works. The process delegate is called for each record and is therefore not a valid location to display a message that should be shown only once.
You would need to add a custom action to achieve that behavior. The scenario you're looking for should be implemented with a processing filter checkbox and processing filter to comply with best practices:
Documentation on processing screens implementation is available here:
https://help-2019r2.acumatica.com/Help?ScreenId=ShowWiki&pageid=a007b57b-af69-4c0f-9fd1-f5d98351035f

EntryProcessor without locking entries

In my application, I'm trying to process data in IMap, the scenario is as follows:
application recieves request (REST for example) with set of keys to be processed
application processes entries with given key and returns result - map where key is original key of the entry and result is calculated
for this scenario IMap.executeOnKeys is almost perfect, with one problem - the entry is locked while being processed - and really it hurts thruput. The IMap is populated on startup and never modified.
Is it possible to process entries without locking them? If possible without sending entries to another node and without causing network overhead (sending 1000 tasks to single node in for-loop)
Here is reference implementation to demonstrate what I'm trying to achieve:
public class Main {
public static void main(String[] args) throws Exception {
HazelcastInstance instance = Hazelcast.newHazelcastInstance();
IMap<String, String> map = instance.getMap("the-map");
// populated once on startup, never modified
for (int i = 1; i <= 10; i++) {
map.put("key-" + i, "value-" + i);
}
Set<String> keys = new HashSet<>();
keys.add("key-1"); // every requst may have different key set, they may overlap
System.out.println(" ---- processing ----");
ForkJoinPool pool = new ForkJoinPool();
// to simulate parallel requests on the same entry
pool.execute(() -> map.executeOnKeys(keys, new MyEntryProcessor("first")));
pool.execute(() -> map.executeOnKeys(keys, new MyEntryProcessor("second")));
System.out.println(" ---- pool is waiting ----");
pool.shutdown();
pool.awaitTermination(5, TimeUnit.MINUTES);
System.out.println(" ------ DONE -------");
}
static class MyEntryProcessor implements EntryProcessor<String, String> {
private String name;
MyEntryProcessor(String name) {
this.name = name;
}
#Override
public Object process(Map.Entry<String, String> entry) {
System.out.println(name + " is processing " + entry);
return calculate(entry); // may take some time, doesn't modify entry
}
#Override
public EntryBackupProcessor<String, String> getBackupProcessor() {
return null;
}
}
}
Thanks in advance
In executeOnKeys the entries are not locked. Maybe you mean that the processing happens on partitionThreads, so that there may be no other processing for the particular key? Anyhow, here's the solution:
Your EntryProcessor should implement:
Offloadable interface -> this means that the partition-thread will be used only for reading the value. The calculation will be done in the offloading thread-pool.
ReadOnly interface -> in this case the EP won't hop on the partition-thread again to save the modification you might have done in the entry. Since your EP does not modify entries, this will increase the performance.

Am I writing duplicate domain logics when synchronise query database in CQRS and EventSourcing?

I'm confused with how to synchronise data to the query database.
Let's say I have an aggregate: CreditAccount and some commands may produce CreditAccountBalanceChangedEvent:
public class CreditAccount extends AbstractAnnotatedAggregateRoot<Long> {
#AggregateIdentifier
private Long id;
private int balance;
private DateRange effectiveDateRange;
#CommandHandler
public CreditAccount(CreateCreditAccountCommand command) {
apply(new CreditAccountCreatedEvent(command.getAccountId(),
command.getEffectiveDateRange()));
apply(new CreditAccountBalanceChangedEvent(command.getAccountId(),
command.getAmount()));
}
#EventHandler
private void on(CreditAccountCreatedEvent event) {
this.id = event.getAccountId();
this.effectiveDateRange = event.getEffectiveDateRange();
}
#EventHandler
private void on(CreditAccountBalanceChangedEvent event) {
//notice this line, some domain logic here
this.balance = add(this.balance, event.getAmount());
}
private int add(int current, int amount) {
return current + amount;
}
}
public class CreditAccountBalanceChangedEvent {
private final long accountId;
private final int amount;
//omitted constructors and getters
}
And everything works fine on the command handler side. And I set off to the query side but I find I'm writing some duplicate domain logic here:
#Transactional
#Slf4j
public class CreditAccountEventHandler {
private CreditAccountReadModelStore creditAccountReadModelStore;
#EventHandler
public void handle(CreditAccountCreatedEvent event) {
log.info("Received " + event);
creditAccountReadModelStore.store(accountDevriveFrom(event));
}
#EventHandler
public void handle(CreditAccountBalanceChangedEvent event) {
log.info("Received " + event);
final CreditAccountReadModel account = creditAccountReadModelStore
.findBy(event.getAccountId());
//notice this line, some domain logic here
account.setBalance(account.getBalance() + event.getAmount());
creditAccountReadModelStore.store(account);
}
//omitted setters and private methods
}
As you may notice, I wrote balance calculation code on both command and query side. My question is that is this inevitable in some situations or I write domain logic in wrong place?
As my study so far, events represent something have occured, so no business logic in them, they're just data holder(but reveal users's intent). So should I add a 'balance' field to CreditAccountBalanceChangedEvent and move balance calculation code to command handler method?
public class CreditAccount extends AbstractAnnotatedAggregateRoot<Long> {
//omitted fields
#CommandHandler
public CreditAccount(CreateCreditAccountCommand command) {
apply(new CreditAccountCreatedEvent(command.getAccountId(),
command.getEffectiveDateRange()));
apply(new CreditAccountBalanceChangedEvent(command.getAccountId(),
command.getAmount(), add(this.balance, command.getAmount())));
}
#EventHandler
private void on(CreditAccountBalanceChangedEvent event) {
//notice this line, some domain logic here
//event.getAmount() is no use here, just for auditing?
this.balance = event.getBalance();
}
}
In this case, I can remove balance calculation on the query side by using event.getBalance().
Sorry for a screen full question, any idea is appreciate.
I see two options.
One is for the command to contain the change in balance, the command handler to calculate the new balance, and the event to contain the new balance. If nothing is recalculated in the event handler, it ensures that if the business rules change in the future, they do not affect your object's history when when it is reconstituted from the events.
An alternative would be to place the business rules in a separate class that is called from both the command handler and the event handler to avoid duplication, and then to version those business rules -- via subclassing for example. So you could have an abstract class called CalculateBalanceRule with a subclass of CalculateBalanceRuleVersion1 that is initially referenced by both. If the rule changes, you create CalculateBalanceRuleVersion2, change your command handler to reference it, but keep the reference to Version1 in your event handler, so that it will always replay the rules it did originally.
The second approach is definitely more maintenance, but can answer HOW something change, not simply WHAT changed, if that's something that's important to your business.
Edit: A third option is for the event to only contain the new balance like in the first option, but to version the events. So you have BalanceChangedEvent, BalanceChangedEvent_v2, and so on. This is the direction I could take, as I don't really care to keep a history of how things changed, but I do need to account for the possibility that the events themselves might take on additional members or rename its members. Logic is then needed to determine which event version to use to reconstitute the object at each step.

Domain Driven Design - I find it hard to come up with a solution for this kind of business logic

Introduction
Let's say I have three Domain Objects:
Proposition
Phase
Task
A Proposition can have one or more Phases. A Phases can have one or more Tasks.
If I completed the last Task in the last Phase, the Proposition must be set to 'Closed'.
In code I've created something like this to complete the last Task of a Phase
//My Business Layer does this:
--------------------------------------
pseudo:
var phase = _phaseRepository.GetById(id);
phase.LastTask.SetComplete();
// My Domain Entities look like this:
------------------------
public class phase()
{
public Task LastTask { get; set; } // not real code of course
}
public class Task()
{
public Phase Phase { get; set; }
public void SetComplete()
{
Phase.IsFinished = true;
}
}
Question
Where do I put the code to set the Proposition to 'Closed'?
Options
I think have a few options:
1) In the Domain Entity: Task.SetComplete
public class Task()
{
public Phase Phase { get; set; }
public void SetComplete()
{
Phase.IsFinished = true;
Phase.Proposition.IsClosed = true;
}
}
2a) In the Business Layer
var phase = _phaseRepository.GetById(id);
phase.LastTask.SetComplete();
var proposition = _propositionRepository.GetById(phase.PropositionId);
proposition.IsClosed = true;
2b) In the Business Layer, maybe a bit nicer way:
var phase = _phaseRepository.GetByIdIncludingProposition(id);
phase.LastTask.SetComplete();
phase.proposition.SetClosed();
3) Let everything pass thru Proposition:
//My Business Layer:
var proposition = _propositionRepository.GetById(id);
proposition.CompleteTask(taskId);
// Domain Object:
public class Proposition()
{
public List<Phase> Phases { get; set; }
public void CompleteTask(long taskId)
{
var task = // psuedo: select relevant task from Phases.Tasks, using taskid
task.SetComplete();
task.Phase.SetFinished();
//psuedo: if task.Phase is last phase in proposition
Phase.Proposition.IsClosed = true;
}
}
About the options
Option 1 is problematic on the line
Phase.Proposition.IsClosed = true;
because Proposition doesn't have to be loaded, and if it's not loaded we get an exception.
Option 2a is problematic because after phase.LastTask.SetComplete() is executed the proposition is not in the correct state. And everywhere in code where one has access to Phase, "phase.LastTask.SetComplete()" can be executed without doing the relevant operations on Proposition.
Option 2b has the same problem as 2a.
Option 3 gives the Proposition class too much responsibilities.
Do you guys have any suggestions?
I'm guessing that Proposition is the aggregate root.A task is part of a proposition in the end and I think that the Tssk should notify that is completed i'd try this approach (basically option 3 a bit modified)
public class Proposition()
{
public Task GetTask(int taskId)
{
//find and return task
}
}
//in business layer
var p= _repository.GetProposition(id);
p.GetTask(taskId).SetComplete();
public class Task
{
public event Action<Task> Completed;
public void SetComplete()
{
if (Completed!=null) Completed(this);
}
The Phase should handle the completed event of Task and when it is triggerd, chek if it's the last task and notify the proporisiton to closeitself. NOw maybe using events is not the best implemetnation, probably the Observer pattern is better, but the main ideas are:
You get a task via a Proposition method (as Proposition is an AR)
Tasks notifies phase when it's finished
Phase notifies proposition when the last task is completed
Proposition should check if there aren't any phases in progress and close itself.
save Proposition in the repository
Come to think of it, this is basically the domain event pattern.

Resources