Bukkit/Spigot) Better MySQL stats updating no lag? - mysql

Hi there I'm having issues with SQL updating player stats which makes very huge lag/timings drop I'm updating the stats on server stop this is my stats code:
public int getDeaths(Player p) {
if (!plugin.getConfig().getBoolean("mysql")) {
return plugin.data.getConfig().getInt("Deaths." + p.getUniqueId() + ".death");
}
if (plugin.getConfig().getBoolean("mysql")) {
int res = 0;
ResultSet result = getMainSQLConnection()
.executeQuery("SELECT * FROM `Account` WHERE playername='" + p.getName() + "'", false);
try {
if (result.next()) {
res = Integer.parseInt(result.getString("deaths"));
}
} catch (SQLException localSQLException) {
}
return res;
}
return 0;
}
public void setDeaths(Player p, int number) {
if (!plugin.getConfig().getBoolean("mysql")) {
plugin.data.getConfig().set("Deaths." + p.getUniqueId() + ".death", number);
plugin.data.save();
}
if (plugin.getConfig().getBoolean("mysql")) {
plugin.sqlConnection.executeUpdate(
"UPDATE `Account` SET deaths='" + number + "' WHERE playername='" + p.getName() + "'");
}
}

If you're getting the death count for a command or something that doesn't require the value to be returned immediately, use a asynchronous scheduler to run the code on a separate thread. For a command you'd do something like this when it is executed:
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
try {
int deaths = getMainSQLConnection()
.executeQuery("SELECT * FROM `Account` WHERE playername='" + p.getName() + "'", false)
.getInt("deaths");
player.sendMessage("Player Deaths: " + deaths);
} catch (SQLException ex) {
player.sendMessage(ChatColor.RED + "That player does not exist!");
}
});
Otherwise, if you need the value in the code for whatever reason, you can use a connection pool such as HikariCP. A connection pool will allow you to maintain multiple connections to your database so when you need to execute a query you don't have to establish a new connection every time (which is what will cause most of the lag).
Better yet, use an asynchronous task in tandem with the connection pool. Here's a good tutorial for learning how to use HikariCP with Bukkit: https://www.spigotmc.org/threads/tutorial-implement-mysql-in-your-plugin-with-pooling.61678
As a side note, for basically zero performance impact on the server, you can load the data asynchronously when a player logs in (using the AsyncPlayerPreLoginEvent). Then store it in memory when they actually join the server (PlayerLoginEvent or PlayerJoinEvent), and remove it when they quit. This way you access the data through memory while they're logged in rather than the database. This is much more complicated and also requires a lot of code to implement correctly though, so I'm not going to go into detail here.

Related

What happens if transaction.Rollback/Commit never called in before closing the connection?

What happens if transaction.Rollback/Commit never called in before closing the connection ?
public DBStatus InsertUpdateUserProfile(Int64 UserID, W_User_Profile oUser)
{
MySqlConnection oMySQLConnecion = null;
MySqlTransaction tr = null;
DBStatus oDBStatus = new DBStatus();
try
{
oMySQLConnecion = new MySqlConnection(DatabaseConnectionString);
if (oMySQLConnecion.State == System.Data.ConnectionState.Closed || oMySQLConnecion.State == System.Data.ConnectionState.Broken)
{
oMySQLConnecion.Open();
}
tr = oMySQLConnecion.BeginTransaction();
if (oMySQLConnecion.State == System.Data.ConnectionState.Open)
{
string Query = #"INSERT INTO user .....................;"
INSERT IGNORE INTO user_role ....................;";
MySqlCommand oCommand = new MySqlCommand(Query, oMySQLConnecion);
oCommand.Transaction = tr;
oCommand.Parameters.AddWithValue("#UserID", UserID);
oCommand.Parameters.AddWithValue("#AddressID", oUser.AddressID);
................
................
int sqlSuccess = oCommand.ExecuteNonQuery();
if (sqlSuccess>0)
{
tr.Commit();
oDBStatus.Type = DBOperation.SUCCESS;
oDBStatus.Message.Add(DBMessageType.SUCCESSFULLY_DATA_UPDATED);
}
oMySQLConnecion.Close();
}
else
{
oDBStatus.Type = DBOperation.ERROR;
oDBStatus.Message.Add(DBMessageType.ERROR_DUE_TO_NO_DB_CONNECTION);
}
return oDBStatus;
}
catch (Exception ex)
{
if (oMySQLConnecion.State == System.Data.ConnectionState.Open)
{
tr.Rollback();
oMySQLConnecion.Close();
}
oDBStatus.Type = DBOperation.ERROR;
oDBStatus.Message.Add(DBMessageType.ERROR_OR_EXCEPTION_OCCURED_WHILE_UPDATING);
oDBStatus.InnerException.Add(ex.Message);
return oDBStatus;
}
}
In the above function, I do Commit if transaction is successful and Rollback if it fails and connection is still on.
If the connection is terminated there is no Rollback. I read many places that says it will be a automatic Rollback if the connection terminates without Commit (what I want). Is this a bad practice ? I could add try-catch after connection establishes but add little code in every similar functions. Does it really necessary ?
What happens if transaction.Rollback/Commit never called in before closing the connection ?
In MySQL the transaction is rolled back. But some other table servers commit it on connection close.
Pro tip: Don't rely on this behavior except as a way to handle a hard crash.

JavaFX MySQL database return sum

I want to use datepicker to select range of dates. This range of dates is then queried in the database where the sum is counted...
My attempt:
public static int dateRange(){
int value = 0;
PreparedStatement stmt = null;
try {
Connection connection = DriverManager.getConnection("", "", "");
stmt = connection.prepareStatement("SELECT SUM(cost) FROM Items WHERE expiration_date between '" + Budget.datePicker1.getValue() + "' and '" + Budget.datePicker2.getValue() + "'");
ResultSet result = stmt.executeQuery();
result.next();
String sum = result.getString(1);
value = Integer.parseInt(sum);
} catch (Exception e){
value += 0;
}
return value;
}
It works. It returns the total if the days are there so to speak. If there are no days in the database as selected in the DatePicker then 0 pops up... But it looks messed up (catch block) and I was wondering if anyone could help me with an alternative solution?
First, since the value returned by the query is an integer value, there is no need to read it as a string and parse it. Just use result.getInt(...).
Second, if you are going to use a PreparedStatement, use it to properly set parameters, instead of building the query out of concatenated strings. Doing the latter exposes your application to SQL injection attacks.
If none of your dates are in range, then the SQL query will return NULL. Calling getInt() on column in a result set that is NULL will return 0, so you will get the result you want in that case anyway. If the previous value you got from a result set was SQL NULL, then calling result.wasNull() will return true, so if you really did need to handle that case separately, you could use that mechanism to do so.
You can do:
public static int dateRange(){
int value = 0;
// Use try-with-resources to make sure resources are released:
try (
Connection connection = DriverManager.getConnection("", "", "");
PreparedStatement stmt = connection.prepareStatement(
"SELECT SUM(cost) FROM Items WHERE expiration_date between ? and ?");
) {
stmt.setDate(1, Date.valueOf(Budget.datePicker1.getValue()));
stmt.setDate(2, Date.valueOf(Budget.datePicker2.getValue()));
ResultSet result = stmt.executeQuery();
result.next();
// Note that if there are no values in range, the SQL result will be NULL
// and getInt() will return 0 anyway.
value = result.getInt(1);
// however, if you need to explicitly check this and do something different
// if nothing is in range, do:
if (result.wasNull()) {
// nothing was in range...
}
} catch (SQLException e){
// this actually indicates something went wrong. Handle it properly.
Logger.getGlobal().log(Level.SEVERE, "Error accessing database", e);
// inform user there was a db error, etc...
}
return value;
}

Why the custom exception message from an exception is not shown in a Xamarin solution?

I have been understanding this example for Xamarin cross-platform mobile development:
https://msdn.microsoft.com/en-us/library/dn879698.aspx
I made an error by copying two times the the API key in the code:
using System;
using System.Threading.Tasks;
namespace XWeatherApp
{
public class Core
{
public static async Task<Weather> GetWeather(string zipCode)
{
//Sign up for a free API key at http://openweathermap.org/appid
string key = "40aabb59f41e9e88db7be4bab11f49f8";
string queryString = "http://api.openweathermap.org/data/2.5/weather?zip="
+ zipCode + ",us&appid=" + key + "&units=imperial";
//Make sure developers running this sample replaced the API key
if (key == "40aabb59f41e9e88db7be4bab11f49f8")
{
throw new ArgumentException("You must obtain an API key from openweathermap.org/appid and save it in the 'key' variable.");
}
dynamic results = await DataService.getDataFromService(queryString).ConfigureAwait(true);
if (results["weather"] != null)
{
Weather weather = new Weather();
weather.Title = (string)results["name"];
weather.Temperature = (string)results["main"]["temp"] + " F";
weather.Wind = (string)results["wind"]["speed"] + " mph";
weather.Humidity = (string)results["main"]["humidity"] + " %";
weather.Visibility = (string)results["weather"][0]["main"];
DateTime time = new System.DateTime(1970, 1, 1, 0, 0, 0, 0);
DateTime sunrise = time.AddSeconds((double)results["sys"]["sunrise"]);
DateTime sunset = time.AddSeconds((double)results["sys"]["sunset"]);
weather.Sunrise = sunrise.ToString() + " UTC";
weather.Sunset = sunset.ToString() + " UTC";
return weather;
}
else
{
return null;
}
}
}
}
Specifically, in the lines after the two comments.
I deployed the app to a physical Android phone. Obviously I got an exception (this was not so obvious after some minutes looking for the failing code).
That exception wasn't displayed in the Output window (in Visual Studio 2017). I just only got this message on screen:
Why don't the custom message for the exception (i.e., You must obtain an API key from openweathermap.org/appid and save it in the 'key' variable.).
Have you tried to use a try/catch?
something like
try{
await GetWeather(string zipCode);
}
catch(Exception ex) {
// here you should have your exception
}

Geotools 13 - error writing to MySQL in linux

Note: Running geotools 13.0.
I have an application that creates Point shapes. My application then writes these features in a shapefile and then into MySQL.
When I run the code under Windows, everything works perfect: the data is properly stored in MySQL and I can use it without any trouble.
When I run the code under Linux, shapefiles are created, but the data is not written in MySQL and the following exception is thrown:
WARNING: class org.geotools.filter.function.Collection_NearestFunction has name conflict betwee 'null' and 'Collection_Nearest'
Exception in thread "main" java.lang.NoSuchFieldError: LINEARIZATION_TOLERANCE
at org.geotools.jdbc.JDBCFeatureReader.init(JDBCFeatureReader.java:211)
at org.geotools.jdbc.JDBCFeatureReader.<init>(JDBCFeatureReader.java:137)
at org.geotools.jdbc.JDBCInsertFeatureWriter.<init>(JDBCInsertFeatureWriter.java:43)
at org.geotools.jdbc.JDBCFeatureStore.getWriterInternal(JDBCFeatureStore.java:280)
at org.geotools.data.store.ContentFeatureStore.getWriter(ContentFeatureStore.java:151)
at org.geotools.data.store.ContentFeatureStore.getWriter(ContentFeatureStore.java:122)
at org.geotools.data.store.ContentFeatureStore.getWriterAppend(ContentFeatureStore.java:263)
at org.geotools.data.store.ContentFeatureStore.addFeatures(ContentFeatureStore.java:242)
at com.technip.projects.gis.GISGenerator.writeShapesMySQL(GISGenerator.java:763)
at com.technip.projects.gis.GISGenerator.generatePlatforms(GISGenerator.java:416)
at com.technip.projects.gis.GISGenerator.createShapefiles(GISGenerator.java:249)
at Machine.run(Machine.java:739)
at Machine.main(Machine.java:329)
My code:
private void writeShapesMySQL(List<SimpleFeature> features) throws IOException {
SimpleFeatureType TYPE = null;
if (!features.isEmpty()) {
TYPE = features.get(0).getType();
// Drop the table if exists
try (Connection con = conf.requestConnection("gis")) {
con.prepareCall("DROP TABLE IF EXISTS " + TYPE.getTypeName() + ";").execute();
} catch (Exception e) {}
if (factory == null) {
initMySQLStore();
}
SimpleFeatureCollection collection = new ListFeatureCollection(TYPE, features);
gisDS.createSchema(TYPE);
Transaction transaction = new DefaultTransaction("create");
String typeName = null;
for (String s : gisDS.getTypeNames()) {
if (s.equalsIgnoreCase(TYPE.getTypeName())) {
typeName = s;
break;
}
}
if (typeName == null) {
log.error("Cannot find the type " + TYPE.getTypeName() + " in the known types: " + String.valueOf(gisDS.getTypeNames()));
throw new IOException("Cannot find type " + TYPE.getTypeName() + " -- in other words, developer sucks.");
}
SimpleFeatureSource featureSource = gisDS.getFeatureSource(typeName);
// System.out.println("SHAPE:"+SHAPE_TYPE);
if (featureSource instanceof SimpleFeatureStore) {
SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
featureStore.setTransaction(transaction);
try {
log.info("Adding " + collection.size() + " features into " + TYPE.getTypeName() + " table.");
featureStore.addFeatures(collection);
transaction.commit();
} catch (Exception problem) {
log.error("Cannot create shapes in MySQL for " + TYPE.getTypeName(), problem);
transaction.rollback();
} finally {
transaction.close();
}
}
} else {
log.warn("Passed empty list to create GIS database.");
}
}
private void initMySQLStore() throws IOException {
factory = new MySQLDataStoreFactory();
Map conMap = new HashMap();
conMap.put("dbtype", "mysql");
conMap.put("host", conf.getDbserver());
conMap.put("port", "3306");
conMap.put("database", "gis");
conMap.put("user", conf.getDbuser());
conMap.put("passwd", conf.getDbpass());
gisDS = factory.createDataStore(conMap);
Map<Class<?>, Integer> classMappings = gisDS.getClassToSqlTypeMappings();
classMappings.put(String.class, new Integer(Types.LONGVARCHAR));
}
My first hint is that MySQL is case sensitive in Linux, but not in Windows. So I checked the created tables both in Linux and Windows, and none of them has a field with such a name (LINEARIZATION_TOLERANCE).
Any hints?
Thanks,
Juan
--UPDATE: PROBLEM SOLVED--
It turned out I had an old .jar from a prior version of geotools in the Linux machine. Removing all old jars fixed the problem.
Problem solved:
It turned out I had an old .jar from a prior version of geotools in the Linux machine. Removing all old jars fixed the problem.
Dismiss this, it is a user error.

net.rim.device.api.io.file.FileIOException: File system out of resources in blackberry

Below code throws net.rim.device.api.io.file.FileIOException: File system out of resources this exception.
Can anyone tell me how it happens?
public Bitmap loadIconFromSDcard(int index) {
FileConnection fcon = null;
Bitmap icon = null;
InputStream is=null;
try {
fcon = (FileConnection) Connector.open(Shikshapatri.filepath + "i"
+ index + ".jpg", Connector.READ);
if (fcon.exists()) {
byte[] content = new byte[(int) fcon.fileSize()];
int readOffset = 0;
int readBytes = 0;
int bytesToRead = content.length - readOffset;
is = fcon.openInputStream();
while (bytesToRead > 0) {
readBytes = is.read(content, readOffset, bytesToRead);
if (readBytes < 0) {
break;
}
readOffset += readBytes;
bytesToRead -= readBytes;
}
EncodedImage image = EncodedImage.createEncodedImage(content,
0, content.length);
image = resizeImage(image, 360, 450);
icon = image.getBitmap();
}
} catch (Exception e) {
System.out.println("Error:" + e.toString());
} finally {
// Close the connections
try {
if (fcon != null)
fcon.close();
} catch (Exception e) {
}
try {
if (is != null)
is.close();
is = null;
} catch (Exception e) {
}
}
return icon;
}
Thanks in advance...
Check this BB dev forum post - http://supportforums.blackberry.com/t5/Java-Development/File-System-Out-of-Resources/m-p/105597#M11927
Basically you should guaranteedly close all connections/streams as soon as you don't need them, because there is a limited number of connection (be it a file connection or http connection) handles in OS. If you execute several loadIconFromSDcard() calls at the same time (from different threads) consider redesign the code to call them sequentially.
UPDATE:
To avoid errors while reading the content just use the following:
byte[] content = IOUtilities.streamToBytes(is);
And since you don't need file connection and input stream any longer just close them right after reading the content (before creating EncodedImage):
is.close();
is = null; // let the finally block know there is no need to try closing it
fcon.close();
fcon = null; // let the finally block know there is no need to try closing it
Minor points:
Also in the finally block it is worth set fcon = null; explicitly after you close it, I believe this can help old JVMs (BB uses Java 1.3 - rather old one) to decide quicker that the object is ready to be garbage collected.
I also believe that the order you close streams in the finally block may be important - I'd change to close is first and then fcon.