LocalDBLoggerExtendedTest.java 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. * Password Management Servlets (PWM)
  3. * http://www.pwm-project.org
  4. *
  5. * Copyright (c) 2006-2009 Novell, Inc.
  6. * Copyright (c) 2009-2021 The PWM Project
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. package password.pwm.util.localdb;
  21. import lombok.Builder;
  22. import lombok.Value;
  23. import org.junit.jupiter.api.BeforeEach;
  24. import org.junit.jupiter.api.Test;
  25. import org.junit.jupiter.api.io.TempDir;
  26. import password.pwm.AppProperty;
  27. import password.pwm.bean.SessionLabel;
  28. import password.pwm.config.AppConfig;
  29. import password.pwm.config.PwmSetting;
  30. import password.pwm.config.stored.StoredConfigurationFactory;
  31. import password.pwm.util.EventRateMeter;
  32. import password.pwm.util.Percent;
  33. import password.pwm.util.java.FileSystemUtility;
  34. import password.pwm.util.java.StringUtil;
  35. import password.pwm.util.java.TimeDuration;
  36. import password.pwm.util.json.JsonFactory;
  37. import password.pwm.util.logging.LocalDBLogger;
  38. import password.pwm.util.logging.LocalDBLoggerSettings;
  39. import password.pwm.util.logging.PwmLogLevel;
  40. import password.pwm.util.logging.PwmLogMessage;
  41. import password.pwm.util.secure.PwmRandom;
  42. import java.io.Serializable;
  43. import java.math.RoundingMode;
  44. import java.nio.file.Path;
  45. import java.text.NumberFormat;
  46. import java.time.Instant;
  47. import java.util.ArrayList;
  48. import java.util.Collection;
  49. import java.util.Collections;
  50. import java.util.HashMap;
  51. import java.util.Map;
  52. import java.util.Random;
  53. import java.util.Timer;
  54. import java.util.TimerTask;
  55. import java.util.concurrent.ArrayBlockingQueue;
  56. import java.util.concurrent.ThreadPoolExecutor;
  57. import java.util.concurrent.TimeUnit;
  58. import java.util.concurrent.atomic.AtomicInteger;
  59. import java.util.function.Supplier;
  60. public class LocalDBLoggerExtendedTest
  61. {
  62. private final NumberFormat numberFormat = NumberFormat.getNumberInstance();
  63. private LocalDBLogger localDBLogger;
  64. private LocalDB localDB;
  65. private AppConfig config;
  66. private final AtomicInteger eventsAdded = new AtomicInteger( 0 );
  67. private final EventRateMeter eventRateMeter = new EventRateMeter( TimeDuration.of( 60, TimeDuration.Unit.SECONDS ).asDuration() );
  68. private static Settings settings;
  69. private Instant startTime;
  70. @TempDir
  71. public Path temporaryFolder;
  72. @BeforeEach
  73. public void setUp() throws Exception
  74. {
  75. final Path localDBPath = FileSystemUtility.createDirectory( temporaryFolder, "test-localdb-logger-test" );
  76. config = AppConfig.forStoredConfig( StoredConfigurationFactory.newConfig() );
  77. localDB = LocalDBFactory.getInstance(
  78. localDBPath,
  79. false,
  80. null,
  81. config
  82. );
  83. //localDB.truncate(LocalDB.DB.EVENTLOG_EVENTS);
  84. //System.out.println(localDB.size(LocalDB.DB.EVENTLOG_EVENTS));
  85. //new TimeDuration(1,TimeUnit.HOURS).pause();
  86. // open localDBLogger based on configuration settings;
  87. {
  88. final LocalDBLoggerSettings settings = LocalDBLoggerSettings.builder()
  89. .maxEvents( Integer.MAX_VALUE )
  90. .maxAge( TimeDuration.of( 1, TimeDuration.Unit.MINUTES ) )
  91. .flags( Collections.emptySet() )
  92. .build();
  93. localDBLogger = new LocalDBLogger( null, localDB, PwmLogLevel.TRACE, settings );
  94. }
  95. settings = Settings.builder()
  96. .threads( 10 )
  97. .testDuration( TimeDuration.of( 1, TimeDuration.Unit.MINUTES ) )
  98. .valueLength( 5000 )
  99. .batchSize( 100 )
  100. .build();
  101. }
  102. private void out( final String output )
  103. {
  104. //System.out.println( JavaHelper.toIsoDate( new Date() ) + " " + output );
  105. }
  106. @Test
  107. public void testBulkAddEvents() throws InterruptedException
  108. {
  109. out( "starting bulk add... " );
  110. out( "settings=" + JsonFactory.get().serialize( settings ) );
  111. startTime = Instant.now();
  112. final Timer timer = new Timer();
  113. final int threadCount = settings.threads;
  114. final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
  115. threadCount,
  116. threadCount,
  117. 1,
  118. TimeUnit.SECONDS,
  119. new ArrayBlockingQueue<>( threadCount + 1 )
  120. );
  121. timer.scheduleAtFixedRate( new DebugOutputTimerTask(), 5 * 1000, 30 * 1000 );
  122. for ( int loopCount = 0; loopCount < threadCount; loopCount++ )
  123. {
  124. threadPoolExecutor.execute( new PopulatorThread() );
  125. }
  126. threadPoolExecutor.shutdownNow();
  127. threadPoolExecutor.awaitTermination( 1, TimeUnit.DAYS );
  128. timer.cancel();
  129. out( "bulk operations completed" );
  130. out( "settings=" + JsonFactory.get().serialize( settings ) );
  131. out( " results=" + JsonFactory.get().serialize( makeResults() ) );
  132. outputDebugInfo();
  133. }
  134. private class PopulatorThread extends Thread
  135. {
  136. @Override
  137. public void run()
  138. {
  139. final RandomValueMaker randomValueMaker = new RandomValueMaker( settings.valueLength );
  140. while ( TimeDuration.fromCurrent( startTime ).isShorterThan( settings.testDuration ) )
  141. {
  142. final Collection<PwmLogMessage> events = makeEvents( randomValueMaker );
  143. for ( final PwmLogMessage logEvent : events )
  144. {
  145. localDBLogger.writeEvent( logEvent );
  146. eventRateMeter.markEvent();
  147. eventsAdded.incrementAndGet();
  148. }
  149. }
  150. }
  151. }
  152. private Collection<PwmLogMessage> makeEvents( final RandomValueMaker randomValueMaker )
  153. {
  154. final int count = settings.batchSize;
  155. final Collection<PwmLogMessage> events = new ArrayList<>();
  156. for ( int i = 0; i < count; i++ )
  157. {
  158. final Supplier<? extends CharSequence> description = ( Supplier<CharSequence> ) randomValueMaker::next;
  159. final PwmLogMessage event = PwmLogMessage.create(
  160. Instant.now(),
  161. LocalDBLogger.class.getName(),
  162. PwmLogLevel.TRACE,
  163. SessionLabel.TEST_SESSION_LABEL,
  164. description,
  165. TimeDuration.ZERO,
  166. null,
  167. "threadName" );
  168. events.add( event );
  169. }
  170. return events;
  171. }
  172. private void outputDebugInfo()
  173. {
  174. final Map<String, String> debugParams = new HashMap<>( Map.of(
  175. "size", StringUtil.formatDiskSize( FileSystemUtility.getFileDirectorySize( localDB.getFileLocation() ) ),
  176. "eventsInDb", figureEventsInDbStat(),
  177. "free", StringUtil.formatDiskSize( FileSystemUtility.diskSpaceRemaining( localDB.getFileLocation() ) ),
  178. "eps", eventRateMeter.rawEps().setScale( 0, RoundingMode.UP ).toString(),
  179. "remain", settings.testDuration.subtract( TimeDuration.fromCurrent( startTime ) ).asCompactString() ) );
  180. localDBLogger.getTailDate().ifPresent( tailDate -> debugParams.put( "tail", TimeDuration.compactFromCurrent( tailDate ) ) );
  181. out( "added " + StringUtil.mapToString( debugParams ) );
  182. }
  183. private String figureEventsInDbStat()
  184. {
  185. final long maxEvents = config.readSettingAsLong( PwmSetting.EVENTS_PWMDB_MAX_EVENTS );
  186. final long eventCount = localDBLogger.getStoredEventCount();
  187. final Percent percent = Percent.of( eventCount, maxEvents );
  188. return numberFormat.format( localDBLogger.getStoredEventCount() ) + "/" + numberFormat.format( maxEvents )
  189. + " (" + percent.pretty( 2 ) + ")";
  190. }
  191. private Results makeResults()
  192. {
  193. return Results.builder()
  194. .dbClass( config.readAppProperty( AppProperty.LOCALDB_IMPLEMENTATION ) )
  195. .duration( TimeDuration.fromCurrent( startTime ).asCompactString() )
  196. .recordsAdded( eventsAdded.get() )
  197. .dbSize( StringUtil.formatDiskSize( FileSystemUtility.getFileDirectorySize( localDB.getFileLocation() ) ) )
  198. .eventsInDb( figureEventsInDbStat() )
  199. .build();
  200. }
  201. private class DebugOutputTimerTask extends TimerTask
  202. {
  203. @Override
  204. public void run()
  205. {
  206. outputDebugInfo();
  207. }
  208. }
  209. @Value
  210. @Builder
  211. private static class Settings implements Serializable
  212. {
  213. private TimeDuration testDuration;
  214. private int threads;
  215. private int valueLength;
  216. private int batchSize;
  217. }
  218. @Value
  219. @Builder
  220. private static class Results implements Serializable
  221. {
  222. private String dbClass;
  223. private String duration;
  224. private int recordsAdded;
  225. private String dbSize;
  226. private String eventsInDb;
  227. }
  228. private static class RandomValueMaker
  229. {
  230. private int outputLength;
  231. final StringBuilder randomValue = new StringBuilder();
  232. final Random random = new Random();
  233. RandomValueMaker( final int outputLength )
  234. {
  235. this.outputLength = outputLength;
  236. randomValue.append( PwmRandom.getInstance().alphaNumericString( outputLength * 50 ) );
  237. }
  238. public String next()
  239. {
  240. final int randomPos = random.nextInt( randomValue.length() - 1 );
  241. randomValue.replace( randomPos, randomPos + 1, String.valueOf( random.nextInt( 9 ) ) );
  242. final int startPos = random.nextInt( randomValue.length() - outputLength );
  243. final int endPos = startPos + outputLength;
  244. return randomValue.substring( startPos, endPos );
  245. }
  246. }
  247. }