here in this case, my logic seems going, even if i...
# cfml-general
g
here in this case, my logic seems going, even if its an error, its still doing an update, while i am trying to do a rollback
Copy code
<cftransaction>
					<cftry>
					<cftransaction action="begin"/>
						<cfset delObject = 'delete the file'>
						<cfif !delObject>
							<cfthrow message="Unable to delete">
						</cfif>	
						<cfcatch type="any">
							<cfthrow/>
						</cfcatch>
					</cftry>
				

					<cftry>
						
						<cfset uploader = 'upload a file'>
						<cfif !isArray(uploader)>
							<cfthrow message="uploader">
						<cfelse>
						<cfquery>
								update query
						</cfquery>
						</cfif>  
						<cftransaction action="commit/">
						<cfcatch type="any">
							<cftransaction action="rollback/">
					</cftry>
					</cftransaction>
d
Unless I'm slipping a gear or two, your tag nesting is foobar. I'm surprised that runs at all.
a
even if it's an error
What sort of error? What's the actual error that it's apparently ignoring? ---- Also: what are you trying to achieve with this:
Copy code
<cftry>
    <--- ... --->
    <cfcatch type="any">
        <cfthrow/>
    </cfcatch>
</cftry>
All that will really do is swap a clear & situation-specific exception for one that isn't? ---- Also... what I'm intrigued by what - specifically -
<cftransaction action="begin"/>
actually does. But that's just for my own education.
@Dave Merrill
Unless I'm slipping a gear or two, your tag nesting is foobar.
Some of those
<cftransaction>
tags are self-closing. It makes it seem like it's whack, but it's actually not.
@gsr it'd really be helpful if you'd put a bit more effort into formatting your code properly to avoid this sort of confusion.
d
the indentation is killing me
i dont think ther'es a closing cfcatch in that last try either
Copy code
<cftransaction action="commit/">
im surprised these transaction tags execute with /s in the action
g
those are some type mistakes,
a
Oh god. I was giving Gavy too much benefit of the doubt, clearly. So - yeah - that's not the code you're actually running, @gsr. And it's messed up in the specific bits you want us to comment on. Can you... as per previous... put more effort in pls?
g
its an old code, what i understood from this code, is that if any error happens, it should end up in catch and rollback the transaction so data is not updated,
d
ive personally never used cftransaction in a try/catch scenario. are you able to refactor this at all?
g
not my coding pattern @Adam Cameron, i do get lots of legacy code to handle so i end up with lots of different trechniques,
a
That's not the point mate
You're asking for help with yer logic, and yer not showing us an accurate representation of the logic
How can we help you if you don't provide us the baseline info we need to look at to actually provide the help?
d
im guessing this block of code youve shared is wrapped in yet another try/catch. try removing the /s from the actions and making sure you have proper syntax with the open/close of each of these tags. the code i pasted in the screenshot here is always going to fire the rollback block, but "rollback/" isnt valid
m
Just some random input, it appears that you are relying on a transaction rollback to undo file system changes, AFAIK transactions are only relative to transactional databases.
g
well, i will try to rephrase what i understood from the code: 1. the user id trying to delete a file 2. if anything goes with deletion like file not found, or timeout, they are throwing the error in catch statement 3. once its in the catch statement, it should abort there itself, but the old code is doing something with throw to actually send it to catch which to me does not make sense, it should just stops there itself and abort it, but somehow the coder is trying to execute the block of code to complete transaction, not making any sense but it is what i see on screen adn interpreted
a
(3) cannot be correct based on the code you provided. This is why I said you need to take more care in providing your sample code. If there is an exception in the first try/catch the one with the delete operation in it - then processing WILL halt because you are throwing another exception in the catch block. If that is not happening, then you are not showing us some of the code. What exception is being thrown in that block? Also: what @Michael Schmidt said. Also: I have verified
<cftransaction action="begin"/>
is pointless.
😅 1
doing something with throw to actually send it to catch
What does that mean?
g
i will go back and rewrite this piece of code again what i usually do and dump this old code, this is a useless code
and remove cftransactions
to avoid confusion
d
if the files are "shared" (not temp files) you might use cflock to prevent other processes from writing/deleting them at the same time as they are being read with cflock. that only works with a single cfinstance processing the same file though. cflock wouldnt stop load-balanced server from messing with it (im making an assumption about the original intent of the cftransaction)
👍 1
e
I would suggest checking the logic of the application code. If userX needs to upload and delete files, and its just thier files, use cffile and cfdirectory. Cftransaction is some kind of WEIRD, hey computers cant do more than one thing throw back from when Macromedia controlled CFML and JRUN was the underlying engine. Instead, use cfdirectory to loop through the list of files for X, then if you want to delete something, delete it with cffile. Even in a heavy load-balanced cluster of clusters, tag your files with unique ids tied to the user and the session. This goes into a far deeper, How should I handle crappy old code, which comes back to, NOTHING YOU ARE DOING IS SO SECRET YOU CAN NOT SHARE ACTUAL CODE. Example <CFTRY> <!--- <CFDIRECTORY action="create" directory="#application.employeeDocs#\#left(qryUser.lastname,4)#\#left(qryUser.firstname,1)#\#empid#\"> <!--- Upload the Employee Image ---> <CFFILE action="upload" filefield="EmployeeImage" destination="#REQUEST.employeeRoot##Left(qryUser.lname, 4)#\#FORM.userID#\" nameconflict="makeunique">
a
Cftransaction is some kind of WEIRD
<cftransaction>
just tells the DB connection to perform transaction operations. There's nothing weird going on.
g
so rewritten using function and wrapped the code inside it, removing transaction, much much better
1
a
OK so was one of the challenges that
<cftransaction>
doesn't quite do what you thought it did?
BTW: good stuff for extracting the code into its own function.
👍 2
e
I fail to see the use case for CFTRANSACTION. Databases and database connectors have overcome all the issues of 1991. Unless you are reading a flat-file database, and even then there are multiple databases that do handle flat-file data, I just fail to see the practical use. It's WEIRD. , programmable slow. Perhaps, "WEIRD" isn't the correct word, " arcane" is the better one. Let's all harken back ta the days when we all paid for BROWSERS,
m
Consider this scenario: I update 5 database records in 5 different tables, I then need to update some external system via a API. That update to the external system fails, due to communications issues. Transactions lets me rollback that update to the database records.
a
Yeah sorry @Evil Ware yer demonstrating a lack of understanding of what DB transactionality is when you say that. It's not an archaic or outmoded concept at all. I think yer just running on a faulty understanding of the topic. This is not meant as a slight. We all have faulty understandings of things.
e
@Adam Cameron I do not claim to be a "CFGOD", and yes, you are correct. I still fail to in small bite-sized flow chart logic to understand how "CFTRANSACTION" is remotely useful when memory is a thousand times faster than executing language to a class file, then the class file goes back to memory to tell how I engine should handle data connector, when at the OS level, its a function of memory, at the database level, its a function of memory. I am sure, there is a logic and a reason, kind of like the reason for pointers in C++. At some point in development, memory was expensive and the OS and hardware didn't correct their own memory issues. Sure, you can still USE pointers in C++, but it comes down to why. I fail to see the WHY, it's just my mental gap. I can't wrap my head around the practical use. 🙂 It's all good.
a
Maybe just read up on it. You have completely the wrong end of the stick as to what a DB transaction is for. It's got absolutely nothing - nothing at all - to do with memory / performance / arcane old ways of doing things. It's not a low level operation at all. This is pretty clear: https://en.wikipedia.org/wiki/Database_transaction But say you have to do this: Program:
Copy code
A: Read a record that has a counter of how many times it was processed
B: Do some other stuff
C: Increment a counter in the record
D: Write record back
Along come two processes: Without a transaction:
Copy code
P1: Step A - counter is 1
P2: Step A - counter is 1
P1: Step B
P2: Step B
P1: Step C - counter is 2
P2: Step C - counter is 2
P1: Step D - writes back 2
P2: Step D - writes back 2 <--- wrong
But in a transaction:
Copy code
P1: creates a transaction
P1: Step A - counter is 1
P2: waits
P1: Step B
P2: waits
P1: Step C - counter is 2
P2: waits
P1: Step D - writes back 2
P1: releases transaction
P2: creates a transaction
P2: Step A - counter is 2
P2: Step B
P2: Step C - counter is 3
P2: Step D - writes back 3 <--- woohoo!
Transactions are also smarter than this very naive implementation, and won't block if they don't need to (or unless they're told to) They're for the same reason application code can have synchronised blocks (
<cflock>
in CFML). But for the DB. They handle race conditions, and the notion of race conditions are not some sort of arcanery from times past. Esp in web applications they're not at all uncommon. And we do need to be dealing with them. Over and above that, transactions allow one to run programs that perform multiple incremental DB write operations, and if any of them fail... back out the whole lot. Or maybe just back out some of it and try it again. Or go "yep I'm done, write all that back now". And throughout the whole process none of the updates impact anyone else going about their DB activity because the changes are "staged" before being committed (or rolled-back). Having a handle on this stuff is pretty fundamental to being a web dev in a data-driven environment.
e
So again, with JDBC being auto-commit, CF using the java API to connect to databases, unless you are writing your own flat file database, why the hell would you need to manually lock a record. Every rational database auto-completes this for you. That for me the disconnect, not the Oh I can run a tag that I could use to build some WONKY ass code to manually and slowly, traverse coldfusion code... Manually stopping, and locking a record, is redundant in an RMDBS, more so with any JDBC driver. While I am sure could point to maybe a small subsect of databases written before the advent of the modern computer age, ACID compliance is even baked into places nobody would think, such as noSQL and SQLLIte. As for memory and OS compliance, it has far more to do than most realize. Your OS, no matter what it is, has a memory threshold where the minimal operational state can be achieved, if EVERYTHING was instant. You see this basic concept in lambda functions, but for sake of not writing a paper on this, once you hit the magic "NUMBER" that OS will be instant if you can call that OS from memory. Luckily, We can with virtualization. So Coldfusion Box, works like this, OS --> Next hit is PROCESS ---> Next hit is DISK --> next hit is OS MEMORY --> next hit is ---> Process route memory ---> Coldfusion Engine container (Apache, Coldbox "WildFly", IBM or Whatever) --> Next hit is DISK --Next Hit Disk --> (Look up Chains of Engine servlet) ---> Next hit Memory (Execution of Application Base Engine) ---> Next hit is Disk --> (Logging + Reading file "Application.CFC" ---> Next Hit is -- Memory (Application Engine Sub logic ---> Next hit is Disk (application compile and or lookup execution of file CFC/CFM ---> Next hit is Disk --> (logs + compile) ---> Next hit is Memory --> Now Repeat ... Now back to my JDBC (Cant wrap my head around the use of a useless use case) So write code, <CFTRANSACTION -- to force coldfusion to go out of its way to hold up an auto-commit to a LOCK. Why would I want to LOCK something as its a MEMORY, DISK, and ultimately, will leave a record "Locked" should the transaction BREAK halfway, such as a power failure or network connectivity issue. Give the DB Admin a job, Ok, go unlock a record as the 1990's called and it wants to bring back SQL 7.0 Server. Again, Not saying you are not correct, as WOW, we need pointers in c++ as its GREAT in breaking all kinds of things, but the average use, its not useful, at all. Lets say you had a million cash registers all updating a database, so instead of input --auto-complete --> you want to batch it (like above) so you are going to use twice the resources to create a possible break in your application flow because? Normal value is pushed JBDC (acid compliant) to RMDBS (pick one) as commit final. Instead, we are going to lock the record, Lock memory to hold the lock record, and hold the disk, to keep the temp lock as a file.CFM reads to OS tmp file, and if I had a power outage or network outage, and everything powers back on, I still have a locked record, so now normal auto-complete or auto-submit functions of Keep Alive will fail, so We have a data object half open, that cant complete that now requires some other process to fix, inefficient. Now let's say you leave everything as is, no CFTRANSACTION, either transaction Failed or transaction Succeeded, there is no Middle ground. If it fails, in the case of the cash register, for example, the person or hardware would TRY again. If it fails on the backend (such as the credit process) the DB has an auto check. We can get into the fine art of redundancy, but in the end, it's something ARCANE, like the blink tag.
a
Oh dear. Do you think that transactions are a CFML thing? Did you even read the article I suggested that explain is?
🙌 1
🤦 1
g
@Evil Ware it’s just a wrapper Consider it as a aluminum foil where inside it is very hot unless it opens If there is no alminium foil The stuff inside it will be hot and cold and so what silly My way of understanding things 😜 It’s not a cfml thing, I agree with @Adam Cameron
e
I guess I put way too much faith in actually reading the documentation as provided by Adobe https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-tags/tags-t/cftransaction.html "_If you do not specify a value for the isolation attribute, ColdFusion uses the default isolation level for the associated database"_ That is the "I" in the "ACID" Both the examples on the page are for "Batch" processing. @Adam Cameron Yes I did read it, again I think it's insanely arcane and resource inefficient. Agree to disagree, whatnot, it's all good. Back to watching digital paint dry..
s
Maybe this example will help you understand... You have code that needs to insert a record in table A and then, using the auto-generated primary key from that insert, insert a record in table B that refers to the record in table A (foreign key). Without transactions (which are a database-level thing, not a CFML thing, BTW), your first insert might succeed but your second insert might fail (the reason doesn't matter) -- now you have an orphan record in table A so you need manual logic in your code to detect the failure of that table B insert and delete the record from table A. And if other threads are active, something else could already have queried table A and gotten that record and be using it (incorrectly) for stuff -- so that "recovery" logic has to cascade through every program that might touch table A while you're trying to handle the failure to insert into table B. Note: every program -- not just your program! With transactions, you can have the database either insert both records or neither. In other words, the database will handle the commit or rollback of those inserts "atomically" for you. Nothing to do with CFML -- this is core SQL (and JDBC) stuff. And it won't matter what programs are touching the database -- or even stored procs executing inside the DB -- transactions keep things consistent. If you don't understand how transactions work, you probably shouldn't be allowed anywhere near a database...
e
See my above comments, CFTRANSACTION is a throwback from 199(0-9) . For MSSQL for example you would use SCOPE in your query https://learn.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql?redirectedfrom=MSDN&amp;view=sql-server-ver16 , for a one-off, and if you where trying to do this all the time, you could use a stored procedure and call it from CFstoredproc. Lets look at what you're asking without CFTRASACTIOn.. Table 1: • id (int, primary key) • fruit_name (varchar) Table 2: • id (int, primary key) • fruit_name (varchar) • table1_id (int, foreign key to Table 1) You would use a stored procedure that would be something like CREATE PROCEDURE insertFruits AS BEGIN DECLARE @table1_id INT: INSERT INTO table1 (fruit_name) VALUES ('Apple'): SET @table1_id = SCOPE_IDENTITY(); INSERT INTO table2 (fruit_name, table1_id) VALUES ('Banana', @table1_id); INSERT INTO table2 (fruit_name, table1_id) VALUES ('Orange', @table1_id); SELECT @@ROWCOUNT AS result; END; And then call that from CFSTOREDPROC. Or go ahead, use CFTRANSACTION. Ill stcik with CFSTOREDPROC as its just cleaner, faster and leaves the DBA's to be DBA's
a
This is hilarious. Please. Keep digging. Just... 🍿 ... keep digging.
s
@Evil Ware This has nothing to do with CFML -- transactions are a database-level thing and sensible developers use them in every language to maintain consistency across multiple operations on a single connection.
And, no, stored procedures are not inherently a solution to the problem that transactions solve (and stored procs bring their own special challenges in terms of versioning, management, deployment etc).
a
I'd be dead keen to see how a stored proc could deal with a situation where one of the steps of the program being run is to call some remote API... or do anything else that isn't the job of a DB.
Also keen to see which bit of which comment of someone else's that EvilWare cherry picks this time to use a straw man to address. It must be beginning to be difficult. Instead of - like - just going "actually yeah, I fucked it lads, didn't I? What a plum I've made of myself in this entire discussion. Duly noted for next time". Which would be the... self-respect-maintaining exit strategy here, I think.
"`<cftransaction>` doesn't even work in every language! CHECKMATE, CORFIELD!!"
🙂 1
s
I figured I would do a sanity check on this convo by asking the missus -- who isn't in IT but used to do marketing and business analysis work and had to use databases from time to time to support that work -- and she was pretty flabbergasted that a developer didn't understand what database transactions were used for...
m
I don't have a problem with someone not understanding transactions I know plenty of people who don't. But when you have plenty of qualified folks from very distinct backgrounds saying the same thing in multiple ways. And you want to argue with them about something it weakens every other conversation.
a
Of course. There is absolutely no shame in not knowing something (this is the place we all start from, after all), nor getting the wrong end of the stick. It's really unhelpful to be spreading bad information on a community help forum though. And it's not a good character trait to refuse to listen to ppl. Not the end of the world though. At this point I'm just enjoying the epicaricacy of the situation, and this is because I am a bad person ;-)
s
Just use NOLOCK on your queries and that'll fix it all, right?
🙈🙉🙊
g
i like
flabbergasted
someone used it as an alternative to
surprised
, not a very common word i hear a lot.
e
Finally looks up - Oh no, I didn't drink the Kool-Aid of every other developer who does it this way---> So, I am wrong, 🙂 Ill stick to my Whiskey and "wrong" understanding of code and stern belief that not all things do __ as well as -----.