sequence.ts 2.71 KB
Newer Older
1
import fs from 'fs';
2
import { FS_ERROR } from '../interfaces/index.js';
3

Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
4
5
const LOCK_FILE_PATH = "/tmp/sequence"
const SEQUENCE_FILE = "/tmp/sequence"
6

Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
7
8
9
10
11
12
13
/**
 * timeout for lock file, we will try to delete it after timeout, in case was not deleted on previous run
 * you can increase this value if you have a lot of processes running on the same machine
 * @type {number}
 * @env TIMEOUT_LOCK_FILE
 */
const TIMEOUT_LOCK_FILE: number = (process.env.TIMEOUT_LOCK_FILE ? ~~process.env.TIMEOUT_LOCK_FILE : undefined) || 10000;
14

Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
15
16
17
18
19
20
/**
 * set delay in ms
 * @param ms 
 * @returns Promise<void> 
 */
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
21

Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
22
23
24
25
26
27
28
29
30
/**
 * create a lock file
 * @param path 
 * @param timeout 
 * @returns Promise<number>
 */
const lockFile = async (path: string, timeout: number = 0): Promise<number> => {
    //TODO if the file exists for long time have to delete it
    // because probably something is broken on previous run
Panagiotis Skarvelis's avatar
Panagiotis Skarvelis committed
31
    // this works but may be not the best solution
Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
32
    if (timeout++ > TIMEOUT_LOCK_FILE) { console.log(timeout, " times loop, try to unlock"); await delay(1000); unlockFile(path) };
33
    const lockPath = `${path}.lock`
34
    try {
Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
35
        return fs.openSync(lockPath, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_RDWR)
36
    } catch (error) {
Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
37
38
39
        const err: FS_ERROR = error as FS_ERROR;
        if (err.code !== "EEXIST") console.log(err.message);
        return lockFile(path, timeout)
40
    }
41
}
Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
42
43
44
45
46
47
48
49

/**
 * unlink the lock file
 * @param path 
 * @param timeout 
 * @returns Promise<void>
 */
const unlockFile = async (path: string, timeout: number = 0): Promise<void> => {
50
    const lockPath = `${path}.lock`
Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
51
52
53
    if (timeout++ > TIMEOUT_LOCK_FILE) { console.log(timeout, " times loop, unlock exit"); return };
    try {
        return fs.unlinkSync(lockPath)
54
    }
Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
55
56
57
58
59
60
61
62
63
    catch (error) {
        const err: FS_ERROR = error as FS_ERROR;
        if (err.code === "ENOENT") {
            return;
        } else {
            console.log(err.message);
            await delay(500);
            return unlockFile(path, timeout++);
        }
64
    }
65
66
67
}

/**
Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
68
69
70
 * update the sequence number on giveb sequence file
 * @param seqfile
 * @param lockfile
71
72
 * @returns number
 */
Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
73
const sequence = (seqfile: string = "", lockfile: string = ""): number => {
74

Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
75
76
77
78
    let SEQF = SEQUENCE_FILE;
    let LFP = LOCK_FILE_PATH;
    if (seqfile !== "") SEQF = seqfile;
    if (lockfile !== "") LFP = lockfile;
Panagiotis Skarvelis's avatar
Panagiotis Skarvelis committed
79

Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
80
81
82
    if (!fs.existsSync(SEQF)) {
        fs.writeFileSync(SEQF, "0")
    }
83

Panagiotis Skarvelis's avatar
cleanup    
Panagiotis Skarvelis committed
84
85
86
87
88
    const abla = lockFile(LFP);
    let seq: number = ~~fs.readFileSync(SEQF); //Read as integer ~~ is synonimus for parseInt
    fs.writeFileSync(SEQF, "" + ++seq);
    const oubla = unlockFile(LFP);
    return seq;
89
90
91
}

export default sequence;