2
votes

I'm setting up a Google Cloud Functions (GCF) function that gets triggered often enough that there are multiple instances running at the same time.

I am getting errors from a readStream the source file of the stream does not exist, but at this point in my program I've actually just created it.

I've made sure the file exists before the start of the stream by console.log()-ing the file JSON, so the file does actually exist. I've also made sure that the file I'm trying to access has finished being written by a previous stream with an await, but no dice.

EDIT: The code now contains the entire script. The section that seems to be throwing the error is the function columnDelete().

var parse = require('fast-csv');
var Storage = require('@google-cloud/storage');
var Transform = require('readable-stream').Transform;

var storage = new Storage();
var bucket = storage.bucket('<BUCKET>');
const DMSs = ['PBS','CDK','One_Eighty','InfoBahn'];


class DeleteColumns extends Transform{
    constructor(){
        super({objectMode:true})
    }
    _transform(row, enc, done){
        //create an array 2 elements shorter than received
        let newRow = new Array(row.length - 2);

        //write all data but the first two columns
        for(let i = 0; i < newRow.length; i++){
            newRow[i] = row[i+2];
        }

        this.push(newRow.toString() + '\n');
        done();
    }
}


function rename(file, originalFile, DMS){
    return new Promise((resolve, reject) => {
        var dealer;
        var date;
        var header = true;
        var parser = parse({delimiter : ",", quote:'\\'});

        //for each row of data
        var stream = originalFile.createReadStream();
        stream.pipe(parser)
        .on('data', (row)=>{


            //if this is the first line do nothing
            if(header){
                header = false;
            }
            //otherwise record the contents of the first two columns and then destroy the stream
            else {
                dealer = row[0].toString().replace('"', '').replace('"', '');
                date = row[1].toString().replace('"', '').replace('"', '');

                stream.end();
            }
        })
        .on('finish', function(){
            var newName = dealer + ' ' + date + '_' + DMS + 'temp.csv';
            //if this was not triggered by the renaming of a file
            if(!file.name.includes(dealer)&&!file.name.includes(':')){
                console.log('Renamed ' + file.name);
                originalFile.copy(newName);
                originalFile.copy(newName.replace('temp',''));
            }else{
                newName = 'Not Renamed';
                console.log('Oops, triggered by the rename');
            }
            resolve(newName);

        });
    });

}
function columnDelete(fileName){
    return new Promise((resolve, reject) =>{
        console.log('Deleting Columns...');
        console.log(bucket.file(fileName));
        var parser = parse({delimiter : ",", quote:'\\'});
        var del = new DeleteColumns();
        var temp = bucket.file(fileName);
        var final = bucket.file(fileName.replace('temp', ''));

        //for each row of data
        temp.createReadStream()
        //parse the csv
        .pipe(parser)
        //delete first two columns
        .pipe(del)
        //write to new file
        .pipe(final.createWriteStream()
            .on('finish', function(){
                console.log('Columns Deleted');
                temp.delete();
                resolve();
            })
        );
    });

}

exports.triggerRename = async(data, context) => {
    var DMS = 'Triple';
    var file = data;
    //if not a temporary file
    if(!file.name.includes('temp')){

        //create a new File object from the name of the data passed
        const originalFile = bucket.file(file.name);

        //identify which database this data is from
        DMSs.forEach(function(database){
            if(file.name.includes(database)){
                DMS = database;
            }
        });
        //rename the file
        var tempName = await rename(file, originalFile, DMS);
        //if it was renamed, delete the extra columns
        if (!tempName.includes('Not Renamed')){
            await columnDelete(tempName);
        }

    } else if(file.name.includes('undefined')){
        console.log(file.name + ' is invalid. Deleted.');
        bucket.file(file.name).delete();
    }
     else {
        console.log( file.name + ' is a temporary file. Did not rename.');
    }
};

What I expect to be output is as below:

Deleting Columns...
Columns Deleted

Nice and simple, letting us know when it has started and finished.

However, I get this instead:

Deleting Columns...
ApiError: No such object: <file> at at Object.parseHttpRespMessage(......)
finished with status: 'crash'

Which is not wanted for obvious reasons. My next thought is to make sure that the file hasn't been deleted by another instance of the script midway through, but to do that I would have to check to see if the file is being used by another stream, which is, to my knowledge, not possible.

Any ideas out there?

1
Please edit the question to show the entire function, including the creation of the object in Cloud Storage, so we can all see exactly what's going on here. - Doug Stevenson
Edited. The code now contains the whole script. - skeetman
Which console log are you saying proves that the file exists? - Doug Stevenson
I had one just below the 'deleting columns' log, but removed it after I proved the file existed. I'll add it back. - skeetman
That log line doesn't prove that a file exists. All it's telling you is that you've created a File object that points to the object, whether or not it exists yet. I'll suggest that your file really just doesn't exist, as the error message suggests. - Doug Stevenson

1 Answers

3
votes

When I was creating the file I called the asynchronous function copy() and moved on, meaning that when trying to access the file it was not finished copying. Unknown to me, the File Object is a reference variable, and did not actually contain the file itself. While the file was copying, the pointer was present but it was pointing to an unfinished file.

Thus, "No Such Object". To fix this, I simply used a callback to make sure that the copying was finished before I was accessing the file.

Thanks to Doug Stevenson for letting me know about the pointer!