1111
1212use OCA \PreviewGenerator \Service \NoMediaService ;
1313use OCA \PreviewGenerator \SizeHelper ;
14+ use OCP \AppFramework \Db \TTransactional ;
1415use OCP \AppFramework \Utility \ITimeFactory ;
1516use OCP \Encryption \IManager ;
1617use OCP \Files \File ;
2627use Symfony \Component \Console \Output \OutputInterface ;
2728
2829class PreGenerate extends Command {
30+ use TTransactional;
31+
2932 /* @return array{width: int, height: int, crop: bool} */
3033 protected array $ specifications ;
3134
@@ -87,13 +90,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
8790 return 1 ;
8891 }
8992
90- if ($ this ->checkAlreadyRunning ()) {
91- $ output ->writeln ('Command is already running. ' );
92- return 2 ;
93- }
94-
95- $ this ->setPID ();
96-
9793 // Set timestamp output
9894 $ formatter = new TimestampFormatter ($ this ->config , $ output ->getFormatter ());
9995 $ output ->setFormatter ($ formatter );
@@ -105,38 +101,44 @@ protected function execute(InputInterface $input, OutputInterface $output): int
105101 }
106102 $ this ->startProcessing ();
107103
108- $ this ->clearPID ();
109-
110104 return 0 ;
111105 }
112106
113107 private function startProcessing (): void {
114108 while (true ) {
115- $ qb = $ this ->connection ->getQueryBuilder ();
116- $ qb ->select ('* ' )
117- ->from ('preview_generation ' )
118- ->orderBy ('id ' )
119- ->setMaxResults (1000 );
120- $ cursor = $ qb ->executeQuery ();
121- $ rows = $ cursor ->fetchAll ();
122- $ cursor ->closeCursor ();
123-
124- if ($ rows === []) {
125- break ;
126- }
109+ /*
110+ * Get and delete the row so that if preview generation fails for some reason the next
111+ * run can just continue. Wrap in transaction to make sure that one row is handled by
112+ * one process only.
113+ */
114+ $ row = $ this ->atomic (function () {
115+ $ qb = $ this ->connection ->getQueryBuilder ();
116+ $ qb ->select ('* ' )
117+ ->from ('preview_generation ' )
118+ ->orderBy ('id ' )
119+ ->setMaxResults (1 );
120+ $ result = $ qb ->executeQuery ();
121+ $ row = $ result ->fetch ();
122+ $ result ->closeCursor ();
123+
124+ if (!$ row ) {
125+ return null ;
126+ }
127127
128- foreach ($ rows as $ row ) {
129- /*
130- * First delete the row so that if preview generation fails for some reason
131- * the next run can just continue
132- */
133128 $ qb = $ this ->connection ->getQueryBuilder ();
134129 $ qb ->delete ('preview_generation ' )
135130 ->where ($ qb ->expr ()->eq ('id ' , $ qb ->createNamedParameter ($ row ['id ' ])));
136131 $ qb ->executeStatement ();
137132
138- $ this ->processRow ($ row );
133+ return $ row ;
134+ }, $ this ->connection );
135+
136+
137+ if (!$ row ) {
138+ break ;
139139 }
140+
141+ $ this ->processRow ($ row );
140142 }
141143 }
142144
@@ -198,33 +200,4 @@ private function processFile(File $file): void {
198200 }
199201 }
200202 }
201-
202- private function setPID (): void {
203- $ this ->config ->setAppValue ($ this ->appName , 'pid ' , posix_getpid ());
204- }
205-
206- private function clearPID (): void {
207- $ this ->config ->deleteAppValue ($ this ->appName , 'pid ' );
208- }
209-
210- private function getPID (): int {
211- return (int )$ this ->config ->getAppValue ($ this ->appName , 'pid ' , -1 );
212- }
213-
214- private function checkAlreadyRunning (): bool {
215- $ pid = $ this ->getPID ();
216-
217- // No PID set so just continue
218- if ($ pid === -1 ) {
219- return false ;
220- }
221-
222- // Get the gid of non running processes so continue
223- if (posix_getpgid ($ pid ) === false ) {
224- return false ;
225- }
226-
227- // Seems there is already a running process generating previews
228- return true ;
229- }
230203}
0 commit comments