I have two entitys: User and UserRole. It is realized as tables in DB and classes with the same names. If I create new user I must create userrole for him. If there is exception during user creation or userrole creation user musn't be created. Question is that I don't know how to set try catch blockes. I have some alternatives:
1)
try
{
UserDA.BeginTransaction();
try
{
UserDA.Save(newUser);
UserDA.CommitTransaction();
}
catch()
{
throw SomeException;
UserDA.RollbackTransaction();
}
UserRoleDA.BeginTransaction();
try
{
UserRoleDA.Save(newUser);
UserRoleDA.CommitTransaction();
}
catch()
{
throw SomeException;
UserRoleDA.RollbackTransaction();
}
}
catch()
{
//catch user creation exception
}
2)
UserDA.BeginTransaction();
try
{
UserDA.Save(newUser);
UserDA.CommitTransaction();
UserRoleDA.BeginTransaction();
try
{
UserRoleDA.Save(newUser);
UserRoleDA.CommitTransaction();
}
catch()
{
throw SomeException;
UserRoleDA.RollbackTransaction();
}
}
catch()
{
//catch
UserDA.RollbackTransaction();
}
May be someone know more correct way.
The general way of doing this is:
Try{
StartTransaction
...do work...
CommitTransaction
}
catch(Exception)
{
RollbackTransaction
}
That way anything thrown while doing work causes the whole transaction to rollback. Only if you reach the end of ...do work... without an exception being thrown does commit get called.
Related
How do I store something from a conn.execute complete block?
https://docs.snowflake.com/en/user-guide/nodejs-driver-use.html#executing-statements
Basically I want to do something like this:
async function checkRecords(conn: Connection, sqlText: string): Promise<number> {
return new Promise ((resolve, reject) => {
try {
conn.execute({
sqlText: sqlText,
complete: function(err, stmt, rows) {
if (err) {
reject(err);
} else {
let ret = parseInt(rows[0].COUNT);
return Promise.resolve(ret);
}
}
});
} catch (err) {
error(err);
}
});
}
I think my question is similar to How can I execute Snowflake statement in async mode using NodeJs Snowflake driver? but I did not find any answer there.
Because of the async nature of the complete I never manage to return the value of the complete block.
I've tried to make it await, I've tried to make the function async and return a promise the point is that when I then call the function it still always ignores the wait (and I see in the log the executions with the actual result after my code that was supposed to wait for it already moved one).
Honestly I did not find any good example of this based in Snowflake SDK so I was wondering if anyone knows of a good example to test thigs.
I've seen a lot of different posts on javascript about this like
How to return the response from an asynchronous call
But honestly I do not get it so I really wanted to know if someone has some example based on the Snowflake SDK I could use for inspiration.
I will close this question but somehow I can't make my code wait on the promise
Basically this code just does nothing.
checkRecords(conn, stmtTextStage).then(value => {
if (value > 0) {
log(`STAGE.${name} contains ${value} unique records!`);
} else {
log(`STAGE.${name} contains no records!`, 'WARN');
}
});
well I did manage to make it work but took me a while because I really don't understand this Promise resolve thing but something like this did work:
async function checkRecords(conn: Connection, sqlText: string): Promise<number> {
return new Promise ((resolve, reject) => {
try {
conn.execute({
sqlText: sqlText,
complete: function(err:SnowflakeError, stmt: Statement, rows: any[]) {
if (err) {
error(`${stmt.getSqlText()} : ${err.code}`);
reject(0);
} else {
if (rows.length === 1) {
resolve(parseInt(rows[0].COUNT));
} else {
error(`${sqlText} returned un-expeted rows!`);
reject(0);
}
}
}
});
} catch (err) {
error(err);
}
});
}
I have this code, hoping to catch all exception during this async method. Somehow, an url exception happened but it was not caught by the catch clause. I have to add a try-catch block around it to catch that exception, don't know why, can someone explain?
public async transform(
): Promise {
const {result} = this.processUrl(url).catch(error => error);
return result;
}
private processUrl(url: string): Promise {
const targetHostname = new URL(url).hostname; // exception thrown here invalid url something
// do something else ...
return Promise.resolve(targetHostname);
}
I was hoping tranform function will never throw exception, but when I provide an invalid url, processUrl method throws out an exception which somehow didn't get caught in the transform function.
I have to do this to catch it.
public async transform(
): Promise {
try {
const {result} = this.processUrl(url).catch(error => error);
} catch(e) {
// invalid url exception got caught
return Promise.resolve(undefined);
}
return result;
}
processURL() is not an async function. Thus, it does not automatically return a promise and does not automatically catch exceptions and turn them into a rejected promise.
So, if an exception is thrown anywhere in processURL(), that exception just propagates upwards as a synchronous exception. If you want to catch that synchronous exception yourself, you will need a try/catch in order to catch it.
Or, you could make processURL() an async function and then it will automatically catch any synchronous exceptions and turned them into a rejected promise that you could then catch with .catch() like this:
private async processUrl(url: string): Promise {
const targetHostname = new URL(url).hostname; // exception thrown here invalid url something
// do something else ...
return Promise.resolve(targetHostname);
}
Or you could catch it manually:
private processUrl(url: string): Promise {
try {
const targetHostname = new URL(url).hostname; // exception thrown here invalid url something
// do something else ...
return Promise.resolve(targetHostname);
} catch(e) {
return Promise.reject(e);
}
}
You may also have an issue here:
const {result} = this.processUrl(url).catch(error => error);
Because your processUrl() function is set up to return a promise. You need to use .then() or await to get the value out of that promise. FYI, if processUrl() is actually entirely synchronous, then it is just complicating matters to make it return a promise when it could just return a value directly.
Trying to make this work:
try{
message.author.send(helpEmbed)
}
catch(error) {
message.channel.send("Your dms are closed, cannot send help message.")
return;
}
message.react('✅')
But catch(error) doesn't work for a promise error (as the actual error log calls it UnhandledPromiseRejectionWarning). And I can't use .catch(() => etc because then I can't put a return inside of it, meaning it won't stop the message.react. And in a try/catch you can't leave the catch() empty.
I am getting the correct output, and indeed, these two operations are being treated as a single transactional unit; where if one fails, both fail.
In this code example: i am doing a transaction of
(1) insert
(2) update
The way I approach it is to nest my db operations inside the .then.
My question is if this code is correct by accident? i am new to promises and knex.
knex.transaction(function(t) {
knex('foo')
.transacting(t)
.insert({id:"asdfk", username:"barry", email:"barry#bar.com"})
.then(function() {
knex('foo')
.where('username','=','bob')
.update({email:"bob#foo.com"})
.then(t.commit, t.rollback)
})
})
.then(function() {
// it worked
},
function() {
// it failed
});
This works, but I feel like I am doing something wrong still. Looking for comments.
You need to return a promise from the inner query in order for the outer chain to be chained with that.
You also swallow any errors because you don't rethrow them - it's better to use .catch() for this reason because it makes it more clearer what is happening - that is what would happen with normal try-catch statement.
knex.transaction(function(t) {
return knex('foo')
.transacting(t)
.insert({id:"asdfk", username:"barry", email:"barry#bar.com"})
.then(function() {
return knex('foo')
.where('username','=','bob')
.update({email:"bob#foo.com"});
})
.then(t.commit)
.catch(function(e) {
t.rollback();
throw e;
})
})
.then(function() {
// it worked
})
.catch(function(e) {
// it failed
});
To understand it better, here's the synchronous version that is being "emulated":
try {
var t = knex.transaction();
try {
knex("foo")
.transacting(t)
.insert({id:"asdfk", username:"barry", email:"barry#bar.com"});
knex("foo")
.where('username','=','bob')
.update({email:"bob#foo.com"});
t.commit();
}
catch (e) {
t.rollback();
// As you can see, if you don't rethrow here
// the outer catch is never triggered
throw e;
}
// It worked
}
catch (e) {
//It failed
}
I was trying out the accepted answer here. It was throwing me some errors like "Transaction query is already complete" and "Database locked". The answer is old, so might be working with previous version. I am using Sqlite3.34.1 and knex0.95.4. The code worked for me with some tweaks. Adding in this thread, It could help someone.
async function process() {
await knex.transaction(function(t) {
return knex('foo')
.transacting(t)
.insert({id:"asdfkg", username:"bob", email:"bob#bar.com"})
.then(function() {
return t('foo').insert({id:"abcd", username:"john", email:"john#bar.com"})
})
.then(function() {
return t('foo')
.where('username','=','bob')
.update({email:"bob#foo.com"});
})
})
.then(function() {
console.log("it worked")
})
.catch(function(e) {
console.log(e)
console.log("It failed")
});
knex.destroy()
}
I think, rollback and commit is taken care by its own, we wont have to specify it explicitly.
I want to add some request timings to the response headers of a request to a Nancy module. I've added some before/after request handlers into the RequestStartup and added the headers no problem (example below) and all was good. I've also added an OnError handler to the ApplicationStartup, to catch errors and return a nice formatted Json response.
pipelines.BeforeRequest += ctx =>
{
ctx.Items.Add("X-RequestStart", DateTime.Now.ToString());
return null;
};
pipelines.AfterRequest += ctx =>
{
var now = DateTime.Now;
try
{
//Not got around to forcing the culture on the datetime parsing yet...
var startTime = DateTime.Parse(ctx.Items["X-RequestStart"].ToString());
ctx.Response.Headers.Add("X-RequestStart", startTime.ToString(CultureInfo.InvariantCulture));
ctx.Response.Headers.Add("X-RequestComplete", now.ToString(CultureInfo.InvariantCulture));
ctx.Response.Headers.Add("X-RequestDuration", (now - startTime).ToString());
}
catch (Exception)
{
ctx.Response.Headers.Add("X-RequestComplete", now.ToString(CultureInfo.InvariantCulture));
}
};
pipelines.OnError += (ctx, exception) =>
{
return ErrorResponse.FromException(exception);
};
What I am noticing however, is that when I have an error thrown, the AfterRequest action is not performed - thus I have no timing headers in to the error response. I've tried moving the before/after request handling to the application startup, but this has no effect either.
The question is in two parts really, firstly, is it possible to get the framework to perform an AfterRequest action after the OnError action has been performed, or is the pipeline set up in a way to prevent this, and secondly, should these before/after request actions be part of the RequestStartup or ApplicationStartup? ApplicationStartup seemed sensible for error handling, whereas RequestStartup seemed sensible for interacting with the response headers as it should be on a per request basis, but I'm not sure if there is a convention for this, or if my assumptions were incorrect.
Unfortunately, this does not seem possible in NancyFx. I took a detailed look at the source code, specifically DefaultRequestDispatcher. Dispatch catches any exceptions thrown during route processing, calls ResolveErrorResult, then gets the response from the negotiator. There does not appear to be an extensibility point to modify responses generated in this way.
In my opinion, this is an oversight that the developers should consider addressing.
public async Task<Response> Dispatch(NancyContext context, CancellationToken cancellationToken)
{
// TODO - May need to make this run off context rather than response .. seems a bit icky currently
var resolveResult = this.Resolve(context);
context.Parameters = resolveResult.Parameters;
context.ResolvedRoute = resolveResult.Route;
try
{
context.Response = await ExecuteRoutePreReq(context, cancellationToken, resolveResult.Before)
.ConfigureAwait(false);
if(context.Response == null)
{
context.Response = await this.routeInvoker.Invoke(resolveResult.Route, cancellationToken,
resolveResult.Parameters, context)
.ConfigureAwait(false);
if (context.Request.Method.Equals("HEAD", StringComparison.OrdinalIgnoreCase))
{
context.Response = new HeadResponse(context.Response);
}
}
await this.ExecutePost(context, cancellationToken, resolveResult.After, resolveResult.OnError)
.ConfigureAwait(false);
}
catch(Exception ex)
{
context.Response = this.ResolveErrorResult(context, resolveResult.OnError, ex);
if (context.Response == null)
{
throw;
}
}
return context.Response;
}
private Response ResolveErrorResult(NancyContext context, Func<NancyContext, Exception, dynamic> resolveResultOnError, Exception exception)
{
if (resolveResultOnError != null)
{
var flattenedException = exception.FlattenInnerExceptions();
var result = resolveResultOnError.Invoke(context, flattenedException);
if (result != null)
{
return this.negotiator.NegotiateResponse(result, context);
}
}
return null;
}