Tutoriel de travail de synchronisation DolphinDB

La fonction de tâche planifiée fournie par la base de données DolphinDB permet au système d'exécuter automatiquement des tâches à une heure et à une fréquence spécifiées. Lorsque nous avons besoin que la base de données exécute automatiquement certains scripts de calcul et d'analyse (tels que le calcul quotidien de la ligne K après la fermeture du marché, la génération de rapports statistiques mensuels), la gestion de la base de données (comme la sauvegarde de la base de données, la synchronisation des données), la gestion du système d'exploitation ( comme la suppression du fichier journal expiré) et d'autres tâches, vous pouvez utiliser cette fonction pour réaliser.

Les emplois chronométrés sont représentés par une fonction, ce qui donne une grande flexibilité dans la définition des tâches. Tout travail qui peut être représenté par une fonction peut être exécuté comme une tâche chronométrée. Les travaux programmés sont soumis via la fonction scheduleJob et exécutés en arrière-plan en fonction de l'heure définie. Une fois le travail créé, les informations de définition relatives au travail sont sérialisées et enregistrées dans le fichier disque du nœud de données. Une fois le nœud redémarré, le système désérialise et charge le travail planifié. Les résultats de chaque exécution d'un travail planifié seront également enregistrés sur le disque du nœud. Nous pouvons utiliser getJobMessage et getJobReturn pour afficher le journal d'exécution et renvoyer la valeur de chaque travail.

1. Présentation des fonctions

1.1 Créer un travail planifié

Créez un travail planifié à l'aide de la fonction scheduleJob . Une fois le travail créé, le système sérialisera les informations de définition du travail et les enregistrera dans un fichier <homeDir>/sysmgmt/jobEditlog.meta. La syntaxe de la fonction est la suivante:

scheduleJob(jobId, jobDesc, jobFunc, scheduledTime, startDate, endDate, frequency, [days])

Ceux à noter sont:

  • Le paramètre jobFunc (fonction de travail) est une fonction sans paramètres.
  • Le paramètre planifiéTime (heure planifiée) peut être un scalaire ou un vecteur de type minute. Lorsqu'il s'agit d'un vecteur, notez que l'intervalle entre deux points temporels adjacents ne peut pas être inférieur à 30 minutes.
  • La valeur de retour de la fonction est l'ID de travail du travail planifié. Si l'ID de travail entré n'est pas le même que l'ID de travail d'un travail planifié existant, le système renvoie l'ID de travail entré. Sinon, ajoutez la date actuelle après jobId, "000", "001", etc. comme suffixes, jusqu'à ce qu'un ID de travail unique soit généré.

Comme nous le savons tous, pour exécuter une fonction, il faut fournir tous les paramètres nécessaires à la fonction. En programmation fonctionnelle, une fonction qui fournit tous les paramètres est en fait une application partielle spéciale ( application partielle) de la fonction d'origine , c'est-à-dire une fonction sans paramètres. Dans DolphinDB, nous utilisons des accolades {} pour indiquer certaines applications.

Diverses fonctions telles que des fonctions personnalisées, des fonctions intégrées, des fonctions de plug-in, des vues de fonction et des fonctions dans les modules peuvent être utilisées comme fonctions de tâche. Par conséquent, les devoirs chronométrés peuvent faire presque tout. Par exemple, utilisez des fonctions personnalisées et des fonctions de plug-in pour le calcul et l'analyse, utilisez la fonction intégrée exécutée pour exécuter un fichier de script, utilisez les fonctions du shell pour effectuer la gestion du système d'exploitation, etc. La tâche de l'exemple suivant appelle une fonction personnalisée getMaxTemperature, qui est utilisée pour calculer la valeur maximale d'un certain indice de température de l'appareil la veille. Le paramètre est le numéro de l'appareil. Lors de la création d'une tâche, utilisez getMaxTemperature {1} pour attribuer une valeur de 1 pour le numéro de l'appareil. Exécuté à 0:00 tous les jours.

def getMaxTemperature(deviceID){
    maxTemp=exec max(temperature) from loadTable("dfs://dolphindb","sensor")
            where ID=deviceID ,ts between (today()-1).datetime():(today().datetime()-1)
    return  maxTemp
}
scheduleJob(`testJob, "getMaxTemperature", getMaxTemperature{1}, 00:00m, today(), today()+30, 'D');

L'exemple suivant exécute un fichier de script. La fonction de travail utilise la fonction d'exécution et spécifie le chemin complet du fichier de script mensuelJob.dos en tant que paramètre. Le travail est exécuté à 0:00 le 1er de chaque mois en 2020.

scheduleJob(`monthlyJob, "Monthly Job 1", run{"/home/DolphinDB/script/monthlyJob.dos"}, 00:00m, 2020.01.01, 2020.12.31, 'M', 1);

L'exemple suivant exécute une commande du système d'exploitation pour supprimer les fichiers journaux. La fonction de travail utilise la fonction shell et spécifie la commande spécifique "rm /home/DolphinDB/server/dolphindb.log" comme paramètre. Les devoirs sont exécutés tous les dimanches à 13 heures.

scheduleJob(`weeklyjob, "rm log", shell{"rm /home/DolphinDB/server/dolphindb.log"}, 1:00m, 2020.01.01, 2021.12.31, 'W', 6);

Dans les applications pratiques, il est un peu gênant d'utiliser des paramètres de fonction et des valeurs de retour de fonction pour l'entrée et la sortie.Notre pratique la plus courante consiste à récupérer les données de la base de données et à stocker les résultats dans la base de données après le calcul. L'exemple suivant consiste à calculer la ligne K de niveau minute après la fermeture du marché tous les jours. Dans la fonction personnalisée computeK, les données de marché sont extraites des trades de table de base de données distribuée et stockées dans la table de base de données distribuée OHLC après le calcul. La fréquence du travail est "W", les jours sont [1,2,3,4,5] et le temps planifié est 15: 00m, ce qui signifie que le travail est exécuté à 15h00 du lundi au vendredi.

def computeK(){
	barMinutes = 7
	sessionsStart=09:30:00.000 13:00:00.000
	OHLC =  select first(price) as open, max(price) as high, min(price) as low,last(price) as close, sum(volume) as volume 
		from loadTable("dfs://stock","trades")
		where time >today() and time < now()
		group by symbol, dailyAlignedBar(timestamp, sessionsStart, barMinutes*60*1000) as barStart
	append!(loadTable("dfs://stock","OHLC"),OHLC)
}
scheduleJob(`kJob, "7 Minutes", computeK, 15:00m, 2020.01.01, 2021.12.31, 'W', [1,2,3,4,5]);

1.2 Requête des travaux de synchronisation

GetScheduledJobs peut être utilisé pour interroger les informations de définition de travail planifiées dans le nœud . La syntaxe de la fonction est la suivante:

getScheduledJobs([jobIdPattern])

Le paramètre jobIdPattern est une chaîne représentant l'ID de travail ou le modèle d'ID de travail. Il prend en charge les caractères génériques "%" et "?". La valeur de retour de la fonction correspond aux informations de travail de minutage sous forme de tableau. Si jobId n'est pas spécifié, tous les travaux sont renvoyés.

Le système enregistrera l'exécution de chaque travail, y compris le journal en cours d'exécution et la valeur de retour du travail planifié. Le journal en cours d'exécution est enregistré dans le fichier jodId.msg et la valeur de retour de la tâche planifiée est enregistrée dans le fichier jobId.object. Ces fichiers sont stockés dans le répertoire <homeDir>/batchJobs. Nous pouvons utiliser getJobMessage et getJobReturn pour afficher le journal en cours et la valeur de retour de chaque travail. Mais faites attention à la valeur du jobID. Premièrement, lors de la création d'un travail, comme mentionné précédemment, si le jobId est le même que l'ID du travail planifié existant, le système ne retournera pas le jobId entré; deuxièmement, pour les travaux qui sera exécuté plusieurs fois, tous les. Lorsque le travail chronométré est exécuté la prochaine fois, l'ID du travail est différent. Par conséquent, nous devons utiliser getRecentJobs pour afficher les travaux de minutage terminés. Par exemple, nous définissons le travail de synchronisation suivant:

def foo(){
	print "test scheduled job at"+ now()
	return now()
}
scheduleJob(`testJob, "foo", foo, 17:00m+0..2*30, today(), today(), 'D');

Après l'exécution getRecentJobs(), obtenez les informations suivantes:

jobId	            jobDesc	startTime	            endTime
------              ------- ----------------------- ----------------------
testJob	            foo1	2020.02.14T17:00:23.636	2020.02.14T17:00:23.639
testJob20200214	    foo1	2020.02.14T17:30:23.908	2020.02.14T17:30:23.910
testJob20200214000  foo1	2020.02.14T18:00:23.148	2020.02.14T18:00:26.749

De cela, nous pouvons voir que l'ID de travail exécuté pour la première fois est "testJob", et la deuxième fois est "testJob20200214" ... à chaque fois qu'il change. Comme indiqué ci-dessous, nous pouvons utiliser getJobMessageet getJobReturnafficher la troisième implémentation:

>getJobMessage(`testJob20200214000);
2020-02-14 18:00:23.148629 Start the job [testJob20200214000]: foo
2020-02-14 18:00:23.148721 test the scheduled job at 2020.02.14T18:00:23.148
2020-02-14 18:00:26.749111 The job is done.

>getJobReturn(`testJob20200214000);
2020.02.14T18:00:23.148

1.3 Supprimer le travail planifié

Pour supprimer un travail planifié, utilisez la fonction deleteScheduledJob . La syntaxe est la suivante:

deleteScheduledJob(jobId)

Le paramètre jobId est l'ID du travail. Avant de supprimer, vous pouvez utiliser getScheduledJobs pour obtenir l'ID de la tâche que vous souhaitez supprimer.

2. Autorisations lorsque le travail de minutage est en cours d'exécution

Quelle identité l'utilisateur se connecte-t-il lors de la création d'un travail chronométré et s'exécute sous cette identité lors de l'exécution d'un travail chronométré. Par conséquent, lorsqu'un utilisateur crée un travail planifié, il est nécessaire de s'assurer que l'utilisateur a l'autorisation d'accéder aux ressources utilisées. Par exemple, si l'utilisateur connecté n'est pas un utilisateur autorisé, il ne peut pas accéder aux fonctions distribuées du cluster. Si les fonctions distribuées du cluster sont utilisées, des erreurs se produiront lors de l'exécution. Dans l'exemple suivant, l'utilisateur guestUser1 n'a pas accès à DFS:

def foo1(){
	print "Test scheduled job "+ now()
	cnt=exec count(*) from loadTable("dfs://FuturesContract","tb")
	print "The count of table is "+cnt
	return cnt
}
login("guestUser1","123456")
scheduleJob(`guestGetDfsjob, "dfs read", foo1, [12:00m, 21:03m, 21:45m], 2020.01.01, 2021.12.31, "D");

Une fois le travail exécuté, utilisez getJobMessage (`guestGetDfsjob) pour interroger, comme indiqué ci-dessous, le travail planifié n'a pas l'autorisation de lire la base de données distribuée:

2020-02-14 21:03:23.193039 Start the job [guestGetDfsjob]: dfs read
2020-02-14 21:03:23.193092 Test the scheduled job at 2020.02.14T21:03:23.193
2020-02-14 21:03:23.194914 Not granted to read table dfs://FuturesContract/tb

Par conséquent, si vous souhaitez exécuter à distance certaines fonctions du nœud de contrôle et accéder à une table distribuée dans le cluster, vous devez vous connecter en tant qu'administrateur (admin) ou autre utilisateur autorisé. Cela peut être fait via la fonction de connexion .

On peut également trouver dans le journal que l'instruction après avoir accédé à la table distribuée n'est pas exécutée, ce qui signifie que si une erreur est rencontrée pendant l'exécution du job, l'exécution sera interrompue. Afin d'empêcher l'apparition d'exceptions et d'arrêter l'exécution des scripts suivants, vous pouvez utiliser des instructions try-catch pour capturer les exceptions. Les informations en cours d'exécution doivent être sorties pendant le processus en cours, qui peuvent être printimprimées, et la sortie sera enregistrée dans le fichier journal jodId.msg.

3. Sérialisation des tâches de chronométrage

Une fois le travail planifié créé, le système conservera l'utilisateur de création (ID utilisateur), l'ID du travail, les informations de description, l'heure de début, la fréquence du travail, la définition du travail, etc. Le chemin de stockage est <homeDir>/sysmgmt/jobEditlog.meta. Le travail est représenté par une fonction de DolphinDB. La définition d'une fonction comprend une série d'instructions, qui à leur tour appellent d'autres fonctions et certains objets globaux, tels que des variables partagées. Les variables partagées sont représentées par des noms lorsqu'elles sont sérialisées. Lors de la désérialisation, la variable partagée doit exister, sinon elle échouera. Les fonctions de travail ou leurs fonctions dépendantes peuvent être divisées en deux catégories selon qu'elles sont compilées: les fonctions compilées incluent des fonctions intégrées et des fonctions de plug-in, et les fonctions de script incluent des fonctions personnalisées, des vues de fonction et des fonctions dans les modules. Les méthodes de sérialisation de ces deux types de fonctions sont différentes, qui sont expliquées séparément ci-dessous.

3.1 Sérialisation des fonctions compilées

Pour la sérialisation des fonctions compilées, seuls le nom de la fonction et le nom du module sont sérialisés. Lors de la désérialisation, ces modules et fonctions seront recherchés dans le système. S'ils ne sont pas trouvés, cela échouera. Par conséquent, si la fonction de plug-in est utilisée dans le travail de synchronisation, elle doit être préchargée avant la désérialisation. L'ordre d'initialisation des ressources des composants liés au travail système et de minutage est le suivant: script d'initialisation au niveau du système (dolphindb.dos), vue des fonctions (vue des fonctions), script de démarrage au niveau de l'utilisateur (startup.dos) et travail de minutage. Le travail planifié est chargé après l'exécution du script de démarrage. Comme le montre l'exemple suivant, le plugin odbc est utilisé dans la fonction jobDemo:

use odbc
def jobDemo(){
	conn = odbc::connect("dsn=mysql_factorDBURL");
}
scheduleJob("job demo","example of init",jobDemo,15:48m, 2019.01.01, 2020.12.31, 'D')

Cependant, le plug-in odbc n'est pas chargé lorsque le système est démarré, donc lors de la lecture du travail planifié, comme cette fonction ne peut pas être reconnue, le journal suivant est généré et le système se ferme.

<ERROR>:Failed to unmarshall the job [job demo]. Failed to deserialize assign statement.. Invalid message format

Après avoir ajouté le code suivant au script de démarrage pour charger le plug-in odbc, le système démarre correctement.

loadPlugin("plugins/odbc/odbc.cfg")

3.2 Sérialisation des fonctions de script

La fonction de script sérialise les paramètres de la fonction et chaque instruction de la définition de fonction. Si l'instruction contient des fonctions de script dépendantes, les définitions de ces fonctions dépendantes seront également sérialisées.

Une fois le travail chronométré créé, si ces fonctions de script sont supprimées ou modifiées, ou si la fonction de script dont elle dépend est modifiée, l'exécution du travail chronométré ne sera pas affectée. Si vous voulez que le travail chronométré soit exécuté selon la nouvelle fonction, vous devez d'abord supprimer le travail chronométré, puis recréer le travail chronométré, sinon l'ancienne fonction sérialisée sera exécutée. Notez que la fonction associée doit également être redéfinie. L'exemple suivant illustre:

  • Exemple 1: la fonction de tâche est modifiée après la création de la tâche chronométrée. Comme indiqué ci-dessous, la fonction de tâche f est redéfinie après la création de la planification:
def f(){
	print "The old function is called " 
}
scheduleJob(`test, "f", f, 11:05m, today(), today(), 'D');
go
def f(){
	print "The new function is called " 
}

Une fois le travail de minutage exécuté, utilisez getJobMessage (`test) pour obtenir les informations suivantes, à partir desquelles vous pouvez voir que le travail de minutage est toujours une ancienne fonction personnalisée.

2020-02-14 11:05:53.382225 Start the job [test]: f
2020-02-14 11:05:53.382267 The old function is called 
2020-02-14 11:05:53.382277 The job is done.
  • Exemple 2: La fonction dont dépend la fonction de tâche est modifiée après la création de la tâche chronométrée. Comme indiqué ci-dessous, la fonction de tâche est la vue de fonction fv et fv appelle la fonction foo. Après la fonction scheduleJob, la fonction fooest redéfinie et la fonction la vue est régénérée:
def foo(){
	print "The old function is called " 
}
def fv(){
	foo()
}
addFunctionView(fv)  

scheduleJob(`testFvJob, "fv", fv, 11:36m, today(), today(), 'D');
go
def foo(){
	print "The new function is called " 
}
dropFunctionView(`fv)
addFunctionView(fv) 

Une fois le travail de minutage exécuté, getJobMessage (`testFvJob) obtient les informations suivantes, à partir desquelles vous pouvez voir que le travail de minutage exécute toujours l'ancienne fonction.

2020-02-14 11:36:23.069892 Start the job [testFvJob]: fv
2020-02-14 11:36:23.069939 The old function is called 
2020-02-14 11:36:23.069951 The job is done.

La même chose est vraie avec les fonctions de module. Nous créons un module printLog.dos avec le contenu suivant:

module printLog
def printLogs(logText){
	writeLog(string(now()) + " : " + logText)
	print "The old function is called"
}

Créez ensuite un travail de minutage pour appeler la fonction printLog :: printLogs:

use printLog
def f5(){
	printLogs("test my log")
}
scheduleJob(`testModule, "f5", f5, 13:32m, today(), today(), 'D');

Modifiez le module comme suit avant d'exécuter le travail de minutage:

module printLog
def printLogs(logText){
	writeLog(string(now()) + " : " + logText)
	print "The new function is called"
}

Une fois le travail de minutage exécuté, getJobMessage (`testModule) obtient les informations suivantes, à partir desquelles nous pouvons voir que le travail de minutage exécute toujours l'ancienne fonction.

2020-02-14 13:32:22.870855 Start the job [testModule]: f5
2020-02-14 13:32:22.871097 The old function is called
2020-02-14 13:32:22.871106 The job is done.

4. Exécutez le fichier de script régulièrement

Lors de la création d'un travail chronométré, si la fonction de travail consiste à exécuter un fichier de script, car seul le nom de fichier est enregistré lors de la sérialisation, le contenu du fichier n'est pas enregistré, vous devez donc placer toutes les fonctions personnalisées dépendantes dans le fichier de script, sinon vous ne pourra pas le trouver. L’exécution de la fonction personnalisée a échoué. Par exemple, créez un fichier de script testjob.dos avec le contenu suivant:

foo()

Exécutez ensuite le script suivant dans l'interface graphique de DolphinDB:

def foo(){
	print ("Hello world!")
}
run "/home/xjqian/testjob.dos"

Les résultats montrent qu'il peut être exécuté normalement:

2020.02.14 13:47:00.992: executing code (line 104-108)...
Hello world!

Ensuite, créez le fichier script de l'exécution du travail planifié, le code est le suivant:

scheduleJob(`dailyfoofile1, "Daily Job 1", run {"/home/xjqian/testjob.dos"}, 16:14m, 2020.01.01, 2020.12.31, 'D');

Mais l'exception suivante s'est produite lors de l'exécution de ce travail:

Exception was raised when running the script [/home/xjqian/testjob.dos]:Syntax Error: [line #3] Cannot recognize the token foo

Cela est dû au fait que la définition de la fonction foo et l'exécution du travail de synchronisation ne sont pas dans la même session et que la définition de la fonction est introuvable lorsque le travail est exécuté. Placez la définition de foo () dans le fichier de script et modifiez le contenu du fichier testjob.dos comme suit:

def foo(){
	print ("Hello world!")
}
foo()

Ensuite, recréez le travail de synchronisation et exécutez ce fichier de script pour le terminer en douceur.

5. Résumé et perspectives

Erreurs courantes et dépannage

  • La fonction de travail fait référence à une variable partagée, mais la variable partagée n'est pas définie avant le chargement du travail. Il est généralement recommandé de définir la variable partagée dans le script de démarrage de l'utilisateur.
  • La fonction de travail fait référence à une fonction du plug-in, mais le plug-in n'est pas chargé avant le chargement du travail. Il est généralement recommandé de définir et de charger le plug-in dans le script de démarrage de l'utilisateur.
  • Exécutez un fichier de script régulièrement, mais la fonction dépendante est introuvable. Le fichier de script doit contenir des fonctions personnalisées dépendantes.
  • L'utilisateur qui crée le travail planifié n'a pas l'autorisation d'accéder aux tables de base de données distribuées. Autorisez l'utilisateur à accéder à la base de données correspondante.
  • Une exception est levée lors de l' utilisation des fonctions scheduleJobgetScheduledJobs et deleteScheduledJob dans le script de démarrage . Lorsque le nœud est démarré, le travail de minutage est initialisé après le script de démarrage , de sorte que les fonctions liées au travail de minutage ne peuvent pas être utilisées dans le script de démarrage

Dans certains cas rares, il peut arriver que lorsque le système redémarre, le chargement du travail de synchronisation échoue ou même que le système ne puisse pas être démarré. En particulier lorsque la version est mise à niveau, les interfaces de fonction telles que les fonctions intégrées et les fonctions de plug-in peuvent changer, ce qui peut entraîner l'échec du chargement du travail, ou certains bogues de compatibilité peuvent entraîner l'échec du redémarrage du système. Par conséquent, nous devons conserver le script qui définit le travail de synchronisation pendant le développement. Si le système ne peut pas être démarré en raison d'une tâche planifiée, vous pouvez d'abord supprimer les fichiers sérialisés du travail planifié <homeDir>/sysmgmt/jobEditlog.meta, puis recréer ces travaux planifiés après le redémarrage du système.

Développement de la fonction de suivi

  • Ajout de la possibilité de parcourir la définition des fonctions de travail et des fonctions dépendantes.
  • Définissez et implémentez la relation de dépendance entre les tâches de synchronisation.

 

Publié le 2020-02-25

Je suppose que tu aimes

Origine blog.csdn.net/qq_41996852/article/details/112302781
conseillé
Classement