Jump to content

aehimself

Members
  • Content Count

    1030
  • Joined

  • Last visited

  • Days Won

    22

Posts posted by aehimself


  1. Just now, Attila Kovacs said:

    @aehimself Okay, why?

    Connecting - 15 seconds. Application stops responding.

    Opening a large dataset with lots of BLOB fields - 1 hour. Application still does not respond.

    Make a change, post it back and commit. Another 5 seconds of white screen. But noone really cares at this point.

     

    For ease of code, put everything on your form. For everything else, there are Threads.

     

    (powered by Loreal and Mastercard 😄)


  2. I started actively using databases in the past couple of years in my applications, and these are the things I wished to know from the beginning:

     

    - Every SQL thing should be in a separate thread. If connection takes 20 seconds or you are downloading a very large dataset over a slow connection, your application will be frozen. Publish important data or the complete dataset objects as properties. Just make sure you disconnect all datasources before any operation and reconnect them after, as data events will start to fire off in an inconsistent state causing AVs most probably.

     

    - When it comes to threads, a golden rule is that each thread should have it's own connection to the database. You also want to make sure that threads are working with different tables or you should have correct transaction isolation set up.

     

    - For service applications I wrote my own "ORM", which is basically a wrapper for a TQuery. Each field the query returns are published as typed properties. So instead of

    query.Edit;
    query.FieldByName('field').AsString := 'Hello, world';
    query.Post;

    I simply can say:

    myTable.Field := 'Hello, world';

    and myTable takes care of everything else. I took this one step further after a couple of DB changes and wrote my own planner. I tell it what kind of tables and fields I want, and it generates MySQL and MSSQL create and delta scripts AND all the myTable classes as Delphi source files. I make a change, I have all the SQL scripts ready to ship with the update and I already can use the new fields in all of MyTable objects... you get the point.

     

    - Depending on the component you use (and how they access the databases) client libraries might not be thread safe or requiring special parameters upon establishing the connection to be thread safe! I found it as a best practice for example to have a critical section when connecting as I had strange access violations when 7 worker threads tried to connect using the same library at the same time.

     

    - If performance is critical, do not use TQuery.FieldByName.

     

    That's all I can think of for now but I'm sure I missed a few. If anything else pops up, I'll try to remember to update this thread.

    • Like 2
    • Thanks 1

  3. If you are willing to look into different components, Zeos has a TZSQLProcessor which does just this. You give it your full query, like

     

    "Create Table A  (ID Integer);

    Insert into A Values(1);

    Update  A set ID=2 Where ID=1;"

     

    Then it splits it up and executes them one by one.

     

    Other than this, you'll have to write the parser yourself. Beware though, splitting up simply by ; is NOT GOING TO WORK.

     

    Examples:

    INSERT INTO MYTABLE (MYSTRINGFIELD) VALUES (";")

    INSERT INTO MYTABLE (MYSTRINGFIELD) VALUES ("x") -- This is just for fun; DROP TABLE MYTABLE

    and so on, you get the point...


  4. Just store the TMemos in a TList when you create them so you don't have to find them later. When generating; have two nested cycles going through that list. Outer from Low(list) To High(list) - 1; inner from outer var to High(list). Not efficient, but easy enough to understand what is going on.

    • Like 1

  5. On 10/13/2020 at 4:27 PM, Tntman said:

    I think that u can make it like client-server app, send converted image ( text ) over socket and display it in ur app.. U can display it in normal image component 

    I guess it depends on the resolution and color depth, but isn't transferring still images and showing them as a video... laggy? I never worked with videos in my entire life, but - as far as I understood - this is why they identify key frames; so they transfer / store only the delta inbetween those.


  6. On 10/15/2020 at 8:17 AM, David Schwartz said:

    But they're part of the main form's overall logic.

    That's a lesson I learned through a number of painful refactorings. NEVER put business logic to UI controls.

     

    On 10/15/2020 at 8:17 AM, David Schwartz said:

    and move chunks of related code into smaller independent units that get merged in at compile time.

    Like...

    Unit Helper;
    
    Interface
    
    Function MyFunction: String;
    Procedure DoSomething;
    
    Implementation
    
    Function MyFunction: String;
    Begin
     Result := 'Hello, world';
    End;
    
    Procedure DoSomething;
    Begin
     Sleep(1000);
    End;
    
    End.

    And then just put Helper in the uses classes of your form, and simply call ShowMessage(MyFunction); ?

     


  7. On 10/16/2020 at 10:52 AM, Stefan Glienke said:

    No, but better they do slowly than relying on the third party no matter how incredible.

    And this is fine, unless a significant amount of them will never be looked at, and just forgotten when 10.4.2 comes out.

    Afaik, there are bugs what Delphi is carrying for more than 5 years.


  8. So, ~ two weeks have passed, no issues experienced so far. Packets are leaving the client and arriving to the server and even decoded properly; which means that the new read-out logic is functioning as planned. As the longest I've seen was about a week with the old solution I'm getting more confident that this was indeed the source of the issue (just to be sure I'll wait some more before considering it fixed, though).

     

    Despite the fact that that the old, TClientSocket / TServerSocket doesn't seem to be the root cause I'm glad I went on and changed them with ICS. Now I have one less concern and a ton of opportunities (like IPV6, which did not even exist when the old components were created by... Borland?)

     

    On 9/28/2020 at 5:25 PM, FPiette said:

    or maybe the sender is simply trying to hang your application (DOS attack).

    When I detect an anomaly (like an undecryptable / corrupted packet) I'm force-closing the connection with the client from the server side (by calling Self.Close, calling from a descendant of TWSocketClient). I'm assuming this also clears and destroys the queue so I don't need to read it empty first...? As my assumption was wrong with my initial receive logic, It's safer to ask I suppose 🙂


  9. Just now, Tntman said:

    but i wanted to make something and learn

    This is something we (almost) all can relate to. Which makes me wonder... how many of us wrote our own password managers, instead of using an existing one? How many "fun projects" we have ready which are never used/published because we used it only to gain experience?

     

    ...or is it just me?

    • Haha 1

  10. On 10/2/2020 at 7:58 AM, Kas Ob. said:

    Simply put, sloppy work done by developers who don't understand the Delphi soul.

    I did this once, when I made a heavily customized DBGrid component. The custom grid had a default popup menu with basic things (copy, copy all, copy with header, etc). So if there was a popup menu assigned design time, I appended my items. If none, I created one.

    Alas, you don't want these options to be available when the grid is empty. So I made a custom onPopup handler to disable these items based on the selected record / field.

    But before assigning the new handler, I saved the old handler in a variable and executed it after I enabled / disabled my menu items.

     

    I mean, there are things what you only can solve like this, but there is a bad way of doing everything.

    However, I too prefer overriding the "DoCall" methods, wherever I can.


  11. 4 hours ago, David Heffernan said:

    Kind of odd that you wouldn't just use a byte, TBytes. 

    Exactly my thoughts. When I was hit with the first encoding issue I switched to TBytes. Easy to handle, but really powerful; especially with the built-in TEncoding classes.

    2 hours ago, A.M. Hoornweg said:

    I do, all the time. But this is about salvaging older (possibly third party) ansi code without a rewrite

    RawByteString, as it was mentioned earlier; but that also requires you to change the library itself


  12. Just now, David Heffernan said:

    It's pretty bad advice. Changing algorithm and implementation without any justification or rationale. Seems like you are advocating trying libraries at random. If every time you encounter an issue you replace the lirbsry, after a while you'll have run out of libraries. 

    Well, it's free and not necessarily change in the algorithm (only if you force the compression to the added LZMA). There is no justification or rationale, just experience:  as I mentioned I'm using this for long years now without any major issues. This can be a coincidence, I agree; this is why I did not say it's error free and guaranteed to bring world peace. Maybe it will work for OP too, maybe not. The decision is his to make to try it, I just offered a potentional candidate I happen to know about.

    Change the libraries until I run out? I fixed a memory leak, added an unaccepted pull request and learned to live with it's limitations - there was no need to look for an other one.

     

    But let's not hijack the topic.


  13. Just now, Uwe Raabe said:

    Seems legit. If you have a horizontal scroll bar there is no need for wrapping anything.

    I can see the logic in there but this is why scrollbars can be disabled. I expected that the horizontal scrollbar will become disabled and word wrapping starts to happen.

     

    Well, anyway. At least it works now.


  14. No, RichEdit works just fine. And I accidentally found the solution... it's the scrollbar.

    Begin
     TextEditor.WordWrap := Not TextEditor.WordWrap;
    End;

    doesn't do anything.

    Begin
     TextEditor.WordWrap := Not TextEditor.WordWrap;
     If TextEditor.WordWrap Then TextEditor.ScrollBars := ssVertical
       Else TextEditor.ScrollBars := ssBoth;
    End;

    works like a charm.


  15. 14 minutes ago, Fr0sT.Brutal said:

    Just foresee that your fixed size could arrive incomplete if it's > 1 byte. I usually read data to buffer to fill header and only then start reading the contents.

    I'm not exactly sure if I got what you meant. The fixed size buffer is only used in the dataavailable evnt to read x bytes out from the socket. Then, there is an inner cycle to process the data in this inner buffer. If a packet ended within it, it copies the missing bytes to a different (variable size; set by the size indicator) buffer, sends that for processing, reads the size indicator and resets it. Until there is nothing else to be processed.

     

    Since I made the latest modifications, this logic seems to function properly - however there was no fragmented packet yet (when the beginning and the end arrive in two different handlers, possibly delayed)


  16. 9 minutes ago, Anders Melander said:

    The ternary operator.

    Yes, yes, yes, that is the name. I use it a lot, and I keep forgetting it's name. I should seriously consider inking it on my wrist.

    Not only the question mark, though.

    That wouldn't help much.

    9 minutes ago, Anders Melander said:

    For me it somehow breaks the flow when I'm reading code.

    That's the beauty of it, no? For me, inline variable declarations are making the code harder to read, for you it's ternary operators. I have a colleague who always writes if (expression == false) instead of if (!expression) because he admitted he misses the exclamation mark about 80% of the times.

     

    All in all - even if you don't use many of them - it's good to have a bigger toolset to choose from when you are coding. We just have our preferences.

     

    Edit: never thought about it, but do inline variables work like in C#? For example

    if (condition)
    {
     String myStr = "";
    }
    myStr = "Hello world";

    will not compile, as inline variables are local to the current block. Does it behave the same way in Delphi?

     

    • Like 1
×