diff --git a/doc/install.md b/doc/install.md index d095dbe..8935adb 100644 --- a/doc/install.md +++ b/doc/install.md @@ -9,3 +9,10 @@ * Point your browser to `http://127.0.0.1:8080` For additional infos (lag,time,players => active mode) on the mapserver interface you should install the [mapserver-mod](mod.md) + +## Performance / Scalability + +For small to medium setups the default values should suffice. +If you have a bigger map (say: above 10 GB) you should configure the mapserver accordingly: + +* See [Mapobject-Database](./mapobjectdb.md) for a scalable mapserver database diff --git a/doc/mapobjectdb.md b/doc/mapobjectdb.md new file mode 100644 index 0000000..b872099 --- /dev/null +++ b/doc/mapobjectdb.md @@ -0,0 +1,14 @@ + +# Mapobject-Database + +The mapobject database is for all the mapobjects (POI, Shops, etc) +Per default it is initialized as a sqlite3 database in `mapserver.sqlite3` + +If you want to store your mapobjects in a proper database or need performance at +scale you can configure a postgresql server in your `world.mt`: + +``` +pgsql_mapserver_connection = host=127.0.0.1 port=5432 user=postgres password=enter dbname=postgres +``` + +The syntax is the same as for `pgsql_connection` diff --git a/server/app/setup.go b/server/app/setup.go index dfae773..e0672c0 100644 --- a/server/app/setup.go +++ b/server/app/setup.go @@ -7,6 +7,7 @@ import ( "mapserver/eventbus" "mapserver/mapblockaccessor" "mapserver/mapblockrenderer" + postgresobjdb "mapserver/mapobjectdb/postgres" sqliteobjdb "mapserver/mapobjectdb/sqlite" "mapserver/params" "mapserver/settings" @@ -108,8 +109,7 @@ func Setup(p params.ParamsType, cfg *Config) *App { //mapserver database if a.Worldconfig[worldconfig.CONFIG_PSQL_MAPSERVER] != "" { - //TODO: Psql connection - + a.Objectdb, err = postgresobjdb.New(a.Worldconfig[worldconfig.CONFIG_PSQL_MAPSERVER]) } else { a.Objectdb, err = sqliteobjdb.New("mapserver.sqlite") } diff --git a/server/mapobjectdb/postgres/logger.go b/server/mapobjectdb/postgres/logger.go new file mode 100644 index 0000000..93d73ed --- /dev/null +++ b/server/mapobjectdb/postgres/logger.go @@ -0,0 +1,11 @@ +package postgres + +import ( + "github.com/sirupsen/logrus" +) + +var log *logrus.Entry + +func init() { + log = logrus.WithFields(logrus.Fields{"prefix": "mapobjectdb.postgres"}) +} diff --git a/server/mapobjectdb/postgres/mapobjects.go b/server/mapobjectdb/postgres/mapobjects.go new file mode 100644 index 0000000..12560d9 --- /dev/null +++ b/server/mapobjectdb/postgres/mapobjects.go @@ -0,0 +1,98 @@ +package postgres + +import ( + "mapserver/coords" + "mapserver/mapobjectdb" +) + +func (db *PostgresAccessor) GetMapData(q *mapobjectdb.SearchQuery) ([]*mapobjectdb.MapObject, error) { + rows, err := db.db.Query(getMapDataPosQuery, + q.Type, + q.Pos1.X, q.Pos1.Y, q.Pos1.Z, + q.Pos2.X, q.Pos2.Y, q.Pos2.Z, + ) + + if err != nil { + return nil, err + } + + defer rows.Close() + + result := make([]*mapobjectdb.MapObject, 0) + var currentObj *mapobjectdb.MapObject + var currentId *int64 + + for rows.Next() { + var id int64 + var Type string + var mtime int64 + var x, y, z int + var posx, posy, posz int + var key, value string + + err = rows.Scan(&id, &Type, &mtime, + &x, &y, &z, &posx, &posy, &posz, + &key, &value, + ) + + if err != nil { + return nil, err + } + + if currentId == nil || *currentId != id { + pos := coords.NewMapBlockCoords(posx, posy, posz) + mo := &mapobjectdb.MapObject{ + MBPos: pos, + Type: Type, + X: x, + Y: y, + Z: z, + Mtime: mtime, + Attributes: make(map[string]string), + } + + currentObj = mo + currentId = &id + + result = append(result, currentObj) + + } + + currentObj.Attributes[key] = value + } + + return result, nil +} + +func (db *PostgresAccessor) RemoveMapData(pos *coords.MapBlockCoords) error { + _, err := db.db.Exec(removeMapDataQuery, pos.X, pos.Y, pos.Z) + return err +} + +func (db *PostgresAccessor) AddMapData(data *mapobjectdb.MapObject) error { + res, err := db.db.Exec(addMapDataQuery, + data.X, data.Y, data.Z, + data.MBPos.X, data.MBPos.Y, data.MBPos.Z, + data.Type, data.Mtime) + + if err != nil { + return err + } + + id, err := res.LastInsertId() + + if err != nil { + return err + } + + for k, v := range data.Attributes { + //TODO: batch insert + _, err := db.db.Exec(addMapDataAttributeQuery, id, k, v) + + if err != nil { + return err + } + } + + return nil +} diff --git a/server/mapobjectdb/postgres/migrate.go b/server/mapobjectdb/postgres/migrate.go new file mode 100644 index 0000000..b3ea329 --- /dev/null +++ b/server/mapobjectdb/postgres/migrate.go @@ -0,0 +1,26 @@ +package postgres + +import ( + "database/sql" + "github.com/sirupsen/logrus" + "mapserver/vfs" + "time" +) + +type PostgresAccessor struct { + db *sql.DB +} + +func (db *PostgresAccessor) Migrate() error { + log.Info("Migrating database") + start := time.Now() + _, err := db.db.Exec(vfs.FSMustString(false, "/sql/postgres_mapobjectdb_migrate.sql")) + if err != nil { + return err + } + t := time.Now() + elapsed := t.Sub(start) + log.WithFields(logrus.Fields{"elapsed": elapsed}).Info("Migration completed") + + return nil +} diff --git a/server/mapobjectdb/postgres/new.go b/server/mapobjectdb/postgres/new.go new file mode 100644 index 0000000..ec17fa0 --- /dev/null +++ b/server/mapobjectdb/postgres/new.go @@ -0,0 +1,17 @@ +package postgres + +import ( + "database/sql" + _ "github.com/mattn/go-sqlite3" +) + +func New(connStr string) (*PostgresAccessor, error) { + db, err := sql.Open("postgres", connStr+" sslmode=disable") + + if err != nil { + return nil, err + } + + sq := &PostgresAccessor{db: db} + return sq, nil +} diff --git a/server/mapobjectdb/postgres/settings.go b/server/mapobjectdb/postgres/settings.go new file mode 100644 index 0000000..19966f2 --- /dev/null +++ b/server/mapobjectdb/postgres/settings.go @@ -0,0 +1,28 @@ +package postgres + +func (db *PostgresAccessor) GetSetting(key string, defaultvalue string) (string, error) { + rows, err := db.db.Query(getSettingQuery, key) + if err != nil { + return "", err + } + + defer rows.Close() + + value := defaultvalue + + if rows.Next() { + + err = rows.Scan(&value) + if err != nil { + return "", err + } + } + + return value, nil + +} + +func (db *PostgresAccessor) SetSetting(key string, value string) error { + _, err := db.db.Exec(setSettingQuery, key, value) + return err +} diff --git a/server/mapobjectdb/postgres/sql.go b/server/mapobjectdb/postgres/sql.go new file mode 100644 index 0000000..aad41fe --- /dev/null +++ b/server/mapobjectdb/postgres/sql.go @@ -0,0 +1,41 @@ +package postgres + +const getMapDataPosQuery = ` +select o.id, o.type, o.mtime, + o.x, o.y, o.z, + o.posx, o.posy, o.posz, + oa.key, oa.value +from objects o +left join object_attributes oa on o.id = oa.objectid +where o.type = $1 +and o.posx >= $2 and o.posy >= $3 and o.posz >= $4 +and o.posx <= $5 and o.posy <= $6 and o.posz <= $7 +order by o.id +` + +const removeMapDataQuery = ` +delete from objects where posx = $1 and posy = $2 and posz = $3 +` + +const addMapDataQuery = ` +insert into +objects(x,y,z,posx,posy,posz,type,mtime) +values($1, $2, $3, $4, $5, $6, $7, $8) +` + +const addMapDataAttributeQuery = ` +insert into +object_attributes(objectid, key, value) +values($1, $2, $3) +` + +const getSettingQuery = ` +select value from settings where key = $1 +` + +const setSettingQuery = ` +insert into settings(key, value) +values($1, $2) +on conflict(key) +do update set value = EXCLUDED.value +` diff --git a/server/static/sql/postgres_mapobjectdb_migrate.sql b/server/static/sql/postgres_mapobjectdb_migrate.sql new file mode 100644 index 0000000..2200cbf --- /dev/null +++ b/server/static/sql/postgres_mapobjectdb_migrate.sql @@ -0,0 +1,30 @@ + +create table if not exists objects( + id serial primary key, + x int, + y int, + z int, + posx int, + posy int, + posz int, + type varchar, + mtime bigint +); + +create index if not exists objects_pos on objects(posx,posy,posz); +create index if not exists objects_pos_type on objects(posx,posy,posz,type); + +create table if not exists object_attributes( + objectid integer not null, + key varchar not null, + value varchar not null, + FOREIGN KEY (objectid) references objects(id) ON DELETE CASCADE, + primary key(objectid, key) +); + +create index if not exists object_attributes_key_value on object_attributes(key, value); + +create table if not exists settings( + key varchar primary key not null, + value varchar not null +);