mirror of https://github.com/apache/kafka.git
				
				
				
			KAFKA-6710: Remove Thread.sleep from LogManager.deleteLogs (#4771)
`Thread.sleep` in `LogManager.deleteLogs` potentially blocks a scheduler thread for up to `log.segment.delete.delay.ms` with a default value of a minute. To avoid this, `deleteLogs` now deletes the logs for which `currentDefaultConfig.fileDeleteDelayMs` has elapsed after the delete was scheduled. Logs for which this interval has not yet elapsed are considered for deletion in the next iteration of `deleteLogs`, which is scheduled sooner if required. Reviewers: Jun Rao <junrao@gmail.com>, Dong Lin <lindong28@gmail.com>, Ted Yu <yuzhihong@gmail.com>
This commit is contained in:
		
							parent
							
								
									514936af6f
								
							
						
					
					
						commit
						f66aebff36
					
				|  | @ -79,13 +79,15 @@ class LogManager(logDirs: Seq[File], | |||
|   private val logsToBeDeleted = new LinkedBlockingQueue[(Log, Long)]() | ||||
| 
 | ||||
|   private val _liveLogDirs: ConcurrentLinkedQueue[File] = createAndValidateLogDirs(logDirs, initialOfflineDirs) | ||||
|   @volatile var currentDefaultConfig = initialDefaultConfig | ||||
|   @volatile private var _currentDefaultConfig = initialDefaultConfig | ||||
|   @volatile private var numRecoveryThreadsPerDataDir = recoveryThreadsPerDataDir | ||||
| 
 | ||||
|   def reconfigureDefaultLogConfig(logConfig: LogConfig): Unit = { | ||||
|     this.currentDefaultConfig = logConfig | ||||
|     this._currentDefaultConfig = logConfig | ||||
|   } | ||||
| 
 | ||||
|   def currentDefaultConfig: LogConfig = _currentDefaultConfig | ||||
| 
 | ||||
|   def liveLogDirs: Seq[File] = { | ||||
|     if (_liveLogDirs.size == logDirs.size) | ||||
|       logDirs | ||||
|  | @ -245,6 +247,9 @@ class LogManager(logDirs: Seq[File], | |||
|     this.logsToBeDeleted.add((log, time.milliseconds())) | ||||
|   } | ||||
| 
 | ||||
|   // Only for testing | ||||
|   private[log] def hasLogsToBeDeleted: Boolean = !logsToBeDeleted.isEmpty | ||||
| 
 | ||||
|   private def loadLog(logDir: File, recoveryPoints: Map[TopicPartition, Long], logStartOffsets: Map[TopicPartition, Long]): Unit = { | ||||
|     debug("Loading log '" + logDir.getName + "'") | ||||
|     val topicPartition = Log.parseTopicPartitionName(logDir) | ||||
|  | @ -704,17 +709,27 @@ class LogManager(logDirs: Seq[File], | |||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    *  Delete logs marked for deletion. | ||||
|    *  Delete logs marked for deletion. Delete all logs for which `currentDefaultConfig.fileDeleteDelayMs` | ||||
|    *  has elapsed after the delete was scheduled. Logs for which this interval has not yet elapsed will be | ||||
|    *  considered for deletion in the next iteration of `deleteLogs`. The next iteration will be executed | ||||
|    *  after the remaining time for the first log that is not deleted. If there are no more `logsToBeDeleted`, | ||||
|    *  `deleteLogs` will be executed after `currentDefaultConfig.fileDeleteDelayMs`. | ||||
|    */ | ||||
|   private def deleteLogs(): Unit = { | ||||
|     var nextDelayMs = 0L | ||||
|     try { | ||||
|       while (!logsToBeDeleted.isEmpty) { | ||||
|         val (removedLog, scheduleTimeMs) = logsToBeDeleted.take() | ||||
|       def nextDeleteDelayMs: Long = { | ||||
|         if (!logsToBeDeleted.isEmpty) { | ||||
|           val (_, scheduleTimeMs) = logsToBeDeleted.peek() | ||||
|           scheduleTimeMs + currentDefaultConfig.fileDeleteDelayMs - time.milliseconds() | ||||
|         } else | ||||
|           currentDefaultConfig.fileDeleteDelayMs | ||||
|       } | ||||
| 
 | ||||
|       while ({nextDelayMs = nextDeleteDelayMs; nextDelayMs <= 0}) { | ||||
|         val (removedLog, _) = logsToBeDeleted.take() | ||||
|         if (removedLog != null) { | ||||
|           try { | ||||
|             val waitingTimeMs = scheduleTimeMs + currentDefaultConfig.fileDeleteDelayMs - time.milliseconds() | ||||
|             if (waitingTimeMs > 0) | ||||
|               Thread.sleep(waitingTimeMs) | ||||
|             removedLog.delete() | ||||
|             info(s"Deleted log for partition ${removedLog.topicPartition} in ${removedLog.dir.getAbsolutePath}.") | ||||
|           } catch { | ||||
|  | @ -730,7 +745,7 @@ class LogManager(logDirs: Seq[File], | |||
|       try { | ||||
|         scheduler.schedule("kafka-delete-logs", | ||||
|           deleteLogs _, | ||||
|           delay = currentDefaultConfig.fileDeleteDelayMs, | ||||
|           delay = nextDelayMs, | ||||
|           unit = TimeUnit.MILLISECONDS) | ||||
|       } catch { | ||||
|         case e: Throwable => | ||||
|  |  | |||
|  | @ -332,5 +332,10 @@ class LogManagerTest { | |||
|       assertNotEquals("File reference was not updated in index", fileBeforeDelete.getAbsolutePath, | ||||
|         fileInIndex.get.getAbsolutePath) | ||||
|     } | ||||
| 
 | ||||
|     time.sleep(logManager.InitialTaskDelayMs) | ||||
|     assertTrue("Logs deleted too early", logManager.hasLogsToBeDeleted) | ||||
|     time.sleep(logManager.currentDefaultConfig.fileDeleteDelayMs - logManager.InitialTaskDelayMs) | ||||
|     assertFalse("Logs not deleted", logManager.hasLogsToBeDeleted) | ||||
|   } | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue