Add Memoization class and supports

This commit is contained in:
2023-10-16 15:10:30 -05:00
parent e633ea8ed1
commit e2859aeb89
3 changed files with 361 additions and 0 deletions

View File

@ -0,0 +1,136 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.utils.memoization;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/*******************************************************************************
** Unit test for Memoization
*******************************************************************************/
class MemoizationTest extends BaseTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void test()
{
Memoization<String, Integer> memoization = new Memoization<>();
memoization.setMaxSize(3);
memoization.setTimeout(Duration.ofMillis(100));
assertThat(memoization.getResult("one")).isEmpty();
memoization.storeResult("one", 1);
assertThat(memoization.getResult("one")).isPresent().get().isEqualTo(1);
////////////////////////////////////////////////////
// store 3 more results - this should force 1 out //
////////////////////////////////////////////////////
memoization.storeResult("two", 2);
memoization.storeResult("three", 3);
memoization.storeResult("four", 4);
assertThat(memoization.getResult("one")).isEmpty();
//////////////////////////////////
// make sure others are present //
//////////////////////////////////
assertThat(memoization.getResult("two")).isPresent().get().isEqualTo(2);
assertThat(memoization.getResult("three")).isPresent().get().isEqualTo(3);
assertThat(memoization.getResult("four")).isPresent().get().isEqualTo(4);
/////////////////////////////////////////////////////////////
// wait more than the timeout, then make sure all are gone //
/////////////////////////////////////////////////////////////
SleepUtils.sleep(150, TimeUnit.MILLISECONDS);
assertThat(memoization.getResult("two")).isEmpty();
assertThat(memoization.getResult("three")).isEmpty();
assertThat(memoization.getResult("four")).isEmpty();
}
/*******************************************************************************
**
*******************************************************************************/
@Test
@Disabled("Slow, so not for CI - but good to demonstrate thread-safety during dev")
void testMultiThread() throws InterruptedException, ExecutionException
{
Memoization<String, Integer> memoization = new Memoization<>();
ExecutorService executorService = Executors.newFixedThreadPool(20);
List<Future<?>> futures = new ArrayList<>();
for(int i = 0; i < 20; i++)
{
int finalI = i;
futures.add(executorService.submit(() ->
{
System.out.println("Start " + finalI);
for(int n = 0; n < 1_000_000; n++)
{
memoization.storeResult(String.valueOf(n), n);
memoization.getResult(String.valueOf(n));
if(n % 100_000 == 0)
{
System.out.format("Thread %d at %,d\n", finalI, +n);
}
}
System.out.println("End " + finalI);
}));
}
while(!futures.isEmpty())
{
Iterator<Future<?>> iterator = futures.iterator();
while(iterator.hasNext())
{
Future<?> next = iterator.next();
if(next.isDone())
{
Object o = next.get();
iterator.remove();
}
}
}
System.out.println("All Done");
}
}