فهرست منبع

[fix] Handle race condition on storage initialization

jherve 1 سال پیش
والد
کامیت
5ececcef9b
4فایلهای تغییر یافته به همراه43 افزوده شده و 4 حذف شده
  1. 14 0
      native/src/job_search/messages.py
  2. 18 2
      native/src/job_search/writer.py
  3. 9 2
      src/Background.purs
  4. 2 0
      src/NativeMessage.purs

+ 14 - 0
native/src/job_search/messages.py

@@ -89,6 +89,20 @@ class JobOfferListMessage(NativeMessage):
         return {"tag": "NativeMessageJobOfferList", "values": [self.job_offers]}
 
 
+@dataclass
+class MessageNotProcessedMessage(NativeMessage):
+    message: NativeMessage
+
+    def serialize(self):
+        return {"tag": "NativeMessageMessageNotProcessed", "values": [self.message]}
+
+
+@dataclass
+class StorageNotReadyMessage(NativeMessage):
+    def serialize(self):
+        return {"tag": "NativeMessageStorageNotReady", "values": []}
+
+
 @dataclass
 class StorageReadyMessage(NativeMessage):
     def serialize(self):

+ 18 - 2
native/src/job_search/writer.py

@@ -21,6 +21,8 @@ from job_search.messages import (
     JobAlreadyExistsMessage,
     CompanyAddedMessage,
     CompanyAlreadyExistsMessage,
+    StorageNotReadyMessage,
+    MessageNotProcessedMessage,
 )
 
 
@@ -37,6 +39,8 @@ class Application:
                     self.read_writer.send_message(JobAddedMessage(values["id"]))
                 except FileExistsError as e:
                     self.read_writer.send_message(JobAlreadyExistsMessage(values["id"]))
+                except AttributeError as e:
+                    self.mark_as_not_processed_if_storage_not_ready(message)
 
             case AddCompanyMessage(values=values):
                 try:
@@ -44,10 +48,15 @@ class Application:
                     self.read_writer.send_message(CompanyAddedMessage(values["name"]))
                 except FileExistsError as e:
                     self.read_writer.send_message(CompanyAlreadyExistsMessage(values["name"]))
+                except AttributeError as e:
+                    self.mark_as_not_processed_if_storage_not_ready(message)
 
             case ListJobsRequestMessage():
-                offers = list(self.job_storage.read_all().values())
-                self.read_writer.send_message(JobOfferListMessage(offers))
+                try:
+                    offers = list(self.job_storage.read_all().values())
+                    self.read_writer.send_message(JobOfferListMessage(offers))
+                except AttributeError as e:
+                    self.mark_as_not_processed_if_storage_not_ready(message)
 
             case InitialConfigurationMessage(jobs_path):
                 self.job_storage = JobStorage(base_dir=Path(jobs_path))
@@ -58,6 +67,13 @@ class Application:
                     LogMessage.error(content=f"Received unhandled message : {message}")
                 )
 
+    def mark_as_not_processed_if_storage_not_ready(self, message):
+        if self.job_storage is None:
+            self.read_writer.send_message(StorageNotReadyMessage())
+            self.read_writer.send_message(MessageNotProcessedMessage(message))
+        else:
+            raise
+
     async def loop_on_messages(self):
         loop = asyncio.get_running_loop()
         while True:

+ 9 - 2
src/Background.purs

@@ -20,6 +20,7 @@ import JobSearchExtension.NativeMessage (ApplicationProcess(..), NativeMessage(.
 import JobSearchExtension.RuntimeMessage (RuntimeMessage(..), onRuntimeMessageAddListener)
 import JobSearchExtension.Storage (clearAllJobs, getJobsPath, storeJob)
 import LinkedIn.Jobs.JobOffer (JobOffer(..))
+import LinkedIn.Loadable (sleep)
 import LinkedIn.Output.Types (Output(..))
 import LinkedIn.PageUrl (PageUrl(..))
 import LinkedIn.UI.Basic.Types (JobOfferId(..))
@@ -32,11 +33,10 @@ main = do
 
   onNativeMessageAddListener port nativeMessageHandler
   onNativeDisconnectAddListener port \p -> log $ "disconnected from native port " <> p.name <> " (" <> p.error <> ")"
+  onRuntimeMessageAddListener $ contentScriptMessageHandler port
 
   sendConfigurationToNative port
 
-  onRuntimeMessageAddListener $ contentScriptMessageHandler port
-
 contentScriptMessageHandler ∷ Port -> RuntimeMessage -> MessageSender → Effect Unit
 contentScriptMessageHandler
   port
@@ -104,9 +104,16 @@ nativeMessageHandler _ (NativeMessageJobOfferList job_offers) = do
   for_ job_offers \jo -> do
     storeJob jo
 
+nativeMessageHandler _ NativeMessageStorageNotReady = log "[bg] waiting for storage"
 nativeMessageHandler port NativeMessageStorageReady = sendMessageToNative port $ NativeMessageListJobsRequest
 nativeMessageHandler port NativeMessageStorageUpdated = sendMessageToNative port $ NativeMessageListJobsRequest
 nativeMessageHandler port (NativeMessageJobAdded _) = sendMessageToNative port $ NativeMessageListJobsRequest
+
+-- TODO : The actual solution is to make the sender stateful
+nativeMessageHandler port (NativeMessageMessageNotProcessed msg) = launchAff_ do
+  sleep 500
+  liftEffect $ sendMessageToNative port msg
+
 nativeMessageHandler _ m = logShow m
 
 sendConfigurationToNative ∷ Port → Effect Unit

+ 2 - 0
src/NativeMessage.purs

@@ -25,6 +25,7 @@ data NativeMessage =
   NativeMessageBackground String
   | NativeMessageLog {level :: String, content :: String}
   | NativeMessageInitialConfiguration {jobsPath :: String}
+  | NativeMessageStorageNotReady
   | NativeMessageStorageReady
   | NativeMessageStorageUpdated
   | NativeMessageAddJob NewJobOffer
@@ -35,6 +36,7 @@ data NativeMessage =
   | NativeMessageJobOfferList (Array NativePythonJobOffer)
   | NativeMessageCompanyAlreadyExists {name :: String}
   | NativeMessageCompanyAdded {name :: String}
+  | NativeMessageMessageNotProcessed NativeMessage
 
 data ApplicationProcess
   = ApplicationProcessLinkedInSimplified