Вопросы по исходному коду

Форум для обсуждения деталей разработки программы SAS.Планета

Модераторы: vdemidov, Tolik

Re: Вопросы по исходному коду

Сообщение vasketsov » 24 июн 2015, 17:01

zed писал(а):как там сделать многопользовательское чтение/запись в метки, нужно думать много и усердно. У тебя как это сделано?

Да в общем-то нечего там особо думать.
Чтение просто тупо напрямую. Пока продолжается чтение, за согласованность данных отвечает сам SQlite. Если снаружи удалят геометрию или чего другое, то потом просто ничего не найдётся вторичным запросом.
При записи открывается транзакция на BeginWrite и на EndWrite закрывается (в зависимости от того, летим ли с исключением или нет, будет rollback или commit), или как там называется эта пара процедур для блокировки.
То есть, как бы ничего специального для этого я и не делал.
Но база открывается не эксклюзивно, а как проходной двор.

Вот настройки открытия базы:
PRAGMA page_size=1024
;
PRAGMA cache_size=100000
;
PRAGMA main.journal_mode=WAL
;
PRAGMA synchronous=NORMAL
;

В принципе, скорее всего не включён journal_mode=WAL, а без него скорее всего никак не взлетит параллельная работа.

Проблема ещё может быть в том, что разные сборки SQLite по-разному ведут себя в случае многопоточности.
Для некоторых можно вообще ничего не лочить, всё само строем полетит в базу.
См. опцию SQLITE_THREADSAFE
http://sqlite.org/compile.html#debug

Сейчас подумалось, что надо будет переделать кэш в памяти (тайловый в хранилищах и даже может быть кэш категорий в метках) на SQlite или беркли, если там есть что-то типа :memory:
Ща тикет нарисую.
vasketsov
Специалист
 
Сообщения: 901
Зарегистрирован: 25 июл 2009, 21:15
Благодарил (а): 0 раз.
Поблагодарили: 193 раз.

Re: Вопросы по исходному коду

Сообщение zed » 24 июн 2015, 17:36

Да, точно - включил WAL и проблема исчезла. Транзакции и до этого уже где надо, были прикручены.

А дефолтные парамтры были такие:

PRAGMA page_size=4096
PRAGMA cache_size=10000
PRAGMA main.journal_mode=DELETE
PRAGMA synchronous=FULL

Вообще, у них там очень подробно расписано что и чего, над каждым методом. Прям загляденье (из SynSQLite3.):
скрытый текст: показать
Код: Выделить всё
  TSQLDataBase = class
  ...
   /// query or change the page size of the database
    // - the page size must be a power of two between 512 and 65536 inclusive
    // - DBOpen method will set the PageSize to 4096 (if the database is not
    // encrypted), which sounds better than the default 1024 value - you should
    // not have to set this property usually
    // - setting this property will only cause an immediate change in the page
    // size if it is issued while the database is still empty, prior to the
    // first CREATE TABLE statement; if this property is used to specify a new
    // page size just prior to running the VACUUM command and if the database
    // is not in WAL journal mode then VACUUM will change the page size to the
    // new value for the newly created database file
    property PageSize: cardinal read GetPageSize write SetPageSize;
    /// query or change the Write-Ahead Logging mode for the database
    // - beginning with version 3.7 of the SQLite3 engine, a new "Write-Ahead Log"
    // option (hereafter referred to as "WAL") is optionaly available
    // - WAL might be very slightly slower (perhaps 1% or 2% slower) than the
    // traditional rollback-journal approach in applications that do mostly reads
    // and seldom write; but WAL provides more concurrency as readers do not block
    // writers and a writer does not block readers. Reading and writing can
    // proceed concurrently. With our SQLite3 framework, it's not needed.
    // - by default, this option is not set: only implement if you really need it,
    // but our SQlite3 framework use locked access to the databse, so there
    // should be no benefit of WAL for the framework; but if you call
    // directly TSQLDatabase instances in your code, it may be useful to you
    property WALMode: Boolean read GetWALMode write SetWALMode;
    /// query or change the SQlite3 file-based syncrhonization mode, i.e. the
    // way it waits for the data to be flushed on hard drive
    // - default smFull is very slow, but achieve 100% ACID behavior
    // - smNormal is faster, and safe until a catastrophic hardware failure occurs
    // - smOff is the fastest, data should be safe if the application crashes,
    // but database file may be corrupted in case of failure at the wrong time
    property Synchronous: TSQLSynchronousMode read GetSynchronous write SetSynchronous;
    /// query or change the SQlite3 file-based locking mode, i.e. the
    // way it locks the file
    // - default lmNormal is ACID and safe
    // - lmExclusive gives better performance in case of a number of write
    // transactions, so can be used to release a mORMot server power: but you
    // won't be able to access the database file from outside the process (like
    // a "normal" database engine)
    property LockingMode: TSQLLockingMode read GetLockingMode write SetLockingMode;

SQLITE_THREADSAFE включён. Список дефайнов и опции сборки obj для статической линковки:
скрытый текст: показать
Код: Выделить всё
  Code below will link all database engine, from amalgamation source file:

- to compile with free Borland C++ compiler 5.5.1 from the command line:
     \dev\bcc\bin\bcc32 -6 -O2 -c -d -u- sqlite3.c
    FastCall use must be set with defining SQLITE3_FASTCALL above, and
     int __cdecl fts3CompareElemByTerm(const void *lhs, const void *rhs)
     \dev\bcc\bin\bcc32 -6 -O2 -c -d -pr -u- sqlite3.c
- to compile for FPC using the MinGW compiler: run c-fpcmingw.bat
    gcc -O2 -c -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS sqlite3.c
    and copy libgcc.a and libkernel32.a from your MinGW folders
- to compile for FPC using gcc under Linux: run c-fpcgcclin.sh
    gcc -O2 -c -lpthread -ldl -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_RTREE sqlite3.c
    and copy the libgcc.a and sqlite3.o (and libc.a if needed) into the linuxlibrary folder and off you go
    For CentOS 7.0, take a look at http://synopse.info/forum/viewtopic.php?pid=13193#p13193

- the following defines must be added in the beginning of the sqlite3.c file:

//#define SQLITE_ENABLE_FTS3
//  this unit is FTS3-ready, but not compiled with it by default
//  if you don't use FTS3, dont define this conditional: you'll spare 50KB of code
//  this conditional is defined at compile time, in order to create sqlite3fts3.obj
#define SQLITE_DEFAULT_MEMSTATUS 0
//  don't need any debug here
#define SQLITE_THREADSAFE 2
//  assuming multi-thread safety is made by caller - in our framework, there is
// only one thread using the database connection at the same time, but there could
// be multiple database connection at the same time (previous was 0 could be unsafe)
#define SQLITE_OMIT_SHARED_CACHE 1
// no need of shared cache in a threadsafe calling model
#define SQLITE_OMIT_AUTOINIT 1
//  sqlite3.initialize() is done in initialization section below -> no AUTOINIT
#define SQLITE_OMIT_DEPRECATED 1
//  spare some code size - is now defined only if compiled without FTS3/FTS4
#ifndef SQLITE_ENABLE_FTS3
  #define SQLITE_OMIT_TRACE 1
#endif
// we don't need sqlite3.profile() and sqlite3.trace() interfaces
#define SQLITE_OMIT_LOAD_EXTENSION 1
// we don't need extension in an embedded engine
#define SQLITE_OMIT_COMPILEOPTION_DIAGS 1
// we don't need Compilation Options Diagnostics in our embedded engine
#define SQLITE_OMIT_PROGRESS_CALLBACK 1
// we don't need sqlite3.progress_handler() API function
#define SQLITE_ENABLE_RTREE 1
// the RTREE extension is now (from v.1.8/3.7) compiled into the engine
//#define SQLITE_OMIT_LOOKASIDE
// even if we use FastMM4/SynScaleMM, LookAside seems mandatory in c source
#define SQLITE_WITHOUT_MSIZE
// _msize() is not available (nor needed) with FastMM4 memory manager

and, in the sqlite3.c source file, the following functions are made external
in order to allow our proprietary but simple and efficient encryption system:

extern int winRead(
  sqlite3.file *id,          /* File to read from */
  void *pBuf,                /* Write content into this buffer */
  int amt,                   /* Number of bytes to read */
  sqlite3_int64 offset       /* Begin reading at this offset */
);

extern int winWrite(
  sqlite3_file *id,         /* File to write into */
  const void *pBuf,         /* The bytes to be written */
  int amt,                  /* Number of bytes to write */
  sqlite3_int64 offset      /* Offset into the file to begin writing at */
);

Under Linux (thanks Alf for the patch!), change the following lines of sqlite3.c:

extern int unixRead(
  sqlite3_file *id,
  void *pBuf,
  int amt,
  sqlite3_int64 offset
);

extern int unixWrite(
  sqlite3_file *id,
  const void *pBuf,
  int amt,
  sqlite3_int64 offset
);
Хитрости GoogleEarth - то, чего вы не знаете о гугле
Аватара пользователя
zed
Гуру
 
Сообщения: 2888
ICQ: 357167611
Зарегистрирован: 16 авг 2008, 20:21
Откуда: Беларусь, Могилёв
Благодарил (а): 89 раз.
Поблагодарили: 525 раз.

Re: Вопросы по исходному коду

Сообщение zed » 24 июн 2015, 21:32

А ещё этот mORMot умеет писать вот такие логи (добавление метки):
Код: Выделить всё
000000000152668B  "  +    TSQLRestServerDB(0290C4F0).URI(BEGIN root/Mark inlen=0)
00000000015266D3  " cache    TSQLDatabase(09D3E730) Marks.db3 cache flushed
0000000001526717  " SQL      TSQLDatabase(09D3E730) 25us Marks.db3 BEGIN TRANSACTION;
0000000001526725  " srvr     TSQLRestServerDB(0290C4F0)   BEGIN root/Mark ORM-Write -> 200 with outlen=0 in 48 us
0000000001526734  "  -    00.000.057
000000000152674E  "  +    TSQLRestServerDB(0290C4F0).URI(GET root inlen=61)
00000000015267EE  " DB       TSQLRestServerDB(0290C4F0) prepared 46us Marks.db3 SELECT ID,Name FROM MarkImage WHERE Name=? LIMIT 1;
00000000015268A8  " SQL      TSQLRestServerDB(0290C4F0) 63us returned 1 row as 63 bytes SELECT ID,Name FROM MarkImage WHERE Name=:('1.png'): LIMIT 1;
00000000015268AD  " res      TSQLDatabase(09D3E730) {"fieldCount":2,"values":["ID","Name",1,"1.png"],"rowCount":1}
00000000015268B6  " srvr     TSQLRestServerDB(0290C4F0)   GET root/ ORM-Get -> 200 with outlen=63 in 121 us
00000000015268BF  "  -    00.000.125
000000000152693B  "  +    TSQLRestServerDB(0290C4F0).URI(POST root/Mark inlen=206)
000000000152699D  " cache    TSQLDatabase(09D3E730) Marks.db3 cache flushed
0000000001526A2F  " DB       TSQLRestServerDB(0290C4F0) prepared 28us Marks.db3 INSERT INTO Mark (Category,Image,Color1,Color2,Scale1,Scale2,Name,Desc,GeoLon,GeoLat,GeoType,GeoCount) VALUES (?,?,?,?,?,?,?,?,?,?,?,?);
0000000001532899  " SQL      TSQLRestServerDB(0290C4F0) 16.63ms  lastInsertedID=29338 INSERT INTO Mark (Category,Image,Color1,Color2,Scale1,Scale2,Name,Desc,GeoLon,GeoLat,GeoType,GeoCount) VALUES (:(1):,:(1):,:(-1493172480):,:(-1509949440):,:(11):,:(32):,:('Метка 25'):,:('24.06.2015 20:25:33'):,:(117.414959):,:(51.306255):,:(1):,:(0):);
00000000015328C1  " srvr     TSQLRestServerDB(0290C4F0)   POST root/Mark ORM-Write -> 201 with outlen=0 in 16734 us
00000000015328D2  "  -    00.016.743
0000000001532915  "  +    TSQLRestServerDB(0290C4F0).URI(PUT root/Mark/29338/Geometry inlen=24)
00000000015329BF  " srvr     TSQLRestServerDB(0290C4F0)   PUT root/Mark ORM-Write -> 200 with outlen=0 in 55 us
00000000015329C7  "  -    00.000.060
00000000015329EF  "  +    TSQLRestServerDB(0290C4F0).URI(POST root/MarkTextInfo inlen=67)
0000000001532A41  " DB       TSQLRestServerDB(0290C4F0) prepared 16us Marks.db3 INSERT INTO MarkTextInfo (RowID,name,desc) VALUES (?,?,?);
000000000153E444  " SQL      TSQLRestServerDB(0290C4F0) 16.25ms  lastInsertedID=29338 INSERT INTO MarkTextInfo (RowID,name,desc) VALUES (:(29338):,:('метка 25'):,:('24.06.2015 20:25:33'):);
000000000153E465  " srvr     TSQLRestServerDB(0290C4F0)   POST root/MarkTextInfo ORM-Write -> 201 with outlen=0 in 16292 us
000000000153E47C  "  -    00.016.302
000000000153E4B2  "  +    TSQLRestServerDB(0290C4F0).URI(POST root/MarkGeometryRect inlen=87)
000000000153E51F  " DB       TSQLRestServerDB(0290C4F0) prepared 19us Marks.db3 INSERT INTO MarkGeometryRect (RowID,Left,Right,Bottom,Top) VALUES (?,?,?,?,?);
0000000001544781  " SQL      TSQLRestServerDB(0290C4F0) 8.59ms  lastInsertedID=29338 INSERT INTO MarkGeometryRect (RowID,Left,Right,Bottom,Top) VALUES (:(29338):,:(117.414959):,:(117.414959):,:(51.306255):,:(51.306255):);
0000000001544799  " srvr     TSQLRestServerDB(0290C4F0)   POST root/MarkGeometryRect ORM-Write -> 201 with outlen=0 in 8640 us
00000000015447AA  "  -    00.008.649
00000000015447D7  "  +    TSQLRestServerDB(0290C4F0).URI(POST root/MarkView inlen=38)
0000000001544841  " DB       TSQLRestServerDB(0290C4F0) prepared 23us Marks.db3 INSERT INTO MarkView (User,Mark,Visible) VALUES (?,?,?);
00000000015486C2  " SQL      TSQLRestServerDB(0290C4F0) 5.46ms  lastInsertedID=29338 INSERT INTO MarkView (User,Mark,Visible) VALUES (:(1):,:(29338):,:(1):);
00000000015486D5  " srvr     TSQLRestServerDB(0290C4F0)   POST root/MarkView ORM-Write -> 201 with outlen=0 in 5502 us
00000000015486E4  "  -    00.005.510
00000000015486FB  "  +    TSQLRestServerDB(0290C4F0).URI(END root inlen=0)
000000000159BF02  " SQL      TSQLDatabase(09D3E730) 116.73ms Marks.db3 COMMIT TRANSACTION;
000000000159BF25  " srvr     TSQLRestServerDB(0290C4F0)   END root/ ORM-Write -> 200 with outlen=0 in 116765 us
000000000159BF39  "  -    00.116.775
Хитрости GoogleEarth - то, чего вы не знаете о гугле
Аватара пользователя
zed
Гуру
 
Сообщения: 2888
ICQ: 357167611
Зарегистрирован: 16 авг 2008, 20:21
Откуда: Беларусь, Могилёв
Благодарил (а): 89 раз.
Поблагодарили: 525 раз.

Re: Вопросы по исходному коду

Сообщение zed » 21 июл 2015, 15:35

Delphi упорно выдаёт варнинг: [DCC Warning] u_MarkDbImplORM.pas(814): W1036 Variable 'VCategoryID' might not have been initialized который появился после того, как я объявил функцию _GetCategoryID как inline. Что не так и как исправить?
Хитрости GoogleEarth - то, чего вы не знаете о гугле
Аватара пользователя
zed
Гуру
 
Сообщения: 2888
ICQ: 357167611
Зарегистрирован: 16 авг 2008, 20:21
Откуда: Беларусь, Могилёв
Благодарил (а): 89 раз.
Поблагодарили: 525 раз.

Re: Вопросы по исходному коду

Сообщение brab » 01 дек 2015, 13:32

подскажите, пожалуйста - где в исходниках код ответственный за запись точек в sqlite?

Хочу попробовать создавать/менять точки из внешней программы через БД. Попытка для начала просто поменять координаты точки руками в таблице провалилась - видимо есть какая-то проверка или доп. данные.

Спасибо
brab
Новичок
 
Сообщения: 2
Зарегистрирован: 01 дек 2015, 13:29
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.

Re: Вопросы по исходному коду

Сообщение zed » 01 дек 2015, 23:38

Конкретно вот этот метод используется для записи меток в базу: TMarkDbImplORMHelper.InsertMarkSQL. Геометрия (меток, полигонов, путей) у нас сохраняется в WKB формате, код конвертера живёт в u_GeometryToWKB.pas.

Помимо этого, у нас в БД сохраняются размеры геометрий (см. CalcGeometrySize) и её тип (точка/полигон/путь: TSQLGeoType).

Плюс ко всему, по геометриям у нас построен RTree индекс, так что вам нужно не забыть обновлять и его, как это сделано в TMarkDbImplORMHelper.UpdateMarkSQL, если вы захотите обновить координаты меток.
Хитрости GoogleEarth - то, чего вы не знаете о гугле
Аватара пользователя
zed
Гуру
 
Сообщения: 2888
ICQ: 357167611
Зарегистрирован: 16 авг 2008, 20:21
Откуда: Беларусь, Могилёв
Благодарил (а): 89 раз.
Поблагодарили: 525 раз.

Re: Вопросы по исходному коду

Сообщение brab » 04 дек 2015, 01:12

zed писал(а):Конкретно вот этот метод используется для записи меток в базу: TMarkDbImplORMHelper.InsertMarkSQL. Геометрия (меток, полигонов, путей) у нас сохраняется в WKB формате, код конвертера живёт в u_GeometryToWKB.pas.

.


Спасибо, оказалось что перспективнее корежить саму программу - чем писать в базу из вне. Как поставить точку я разобрался, работает. Нарыл в исходниках такую функцию

vc:=FGeometryLonLatFactory.CreateLonLatPolygonCircleByPoint(vdatum,VSourceLonLat,1000);

но как я понимаю это создается только сам объект, на карте он не появляется, в базе не сохраняется. С точкой сделано вот так:

VPoint := FGeometryLonLatFactory.CreateLonLatPoint(VLonLat);
FMarkDBGUI.SaveMarkModal(nil, VPoint);

Но аналога FMarkDBGUI.SaveMarkModal(nil, VPoint) для полигона я не нашел. А лучше бы было сохранять полигон напрямую, без вызова диалога... Да и точку так же...

Спасибо!
brab
Новичок
 
Сообщения: 2
Зарегистрирован: 01 дек 2015, 13:29
Благодарил (а): 0 раз.
Поблагодарили: 0 раз.

Re: Вопросы по исходному коду

Сообщение zed » 04 дек 2015, 10:20

brab писал(а):Но аналога FMarkDBGUI.SaveMarkModal(nil, VPoint) для полигона я не нашел.

Этот метод принимает на вход любую геометрию, а не только точки.
brab писал(а):А лучше бы было сохранять полигон напрямую, без вызова диалога... Да и точку так же...

Ну так сходите и посмотрите, как реализован метод SaveMarkModal и избавьтесь там от вызова гуишной части. К примеру, можете посмотреть как сделан импорт меток через сообщения WM_COPYDATA: ProcessImportPlacemark - там сперва парсятся входные параметры в формате "{name}";({lon},{lat});"{desc}", а потом идёт непосредственное добавление метки в БД.

Вообще, у SAS можно оторвать всю гуишную часть и не создавать главную форму вообще. Достаточно создать объект класса TGlobalState, взять оттуда базу меток (IMarkSystem) и делать с ней всё, что душе заблагорассудится.
Хитрости GoogleEarth - то, чего вы не знаете о гугле
Аватара пользователя
zed
Гуру
 
Сообщения: 2888
ICQ: 357167611
Зарегистрирован: 16 авг 2008, 20:21
Откуда: Беларусь, Могилёв
Благодарил (а): 89 раз.
Поблагодарили: 525 раз.

Re: Вопросы по исходному коду

Сообщение DJ VK » 03 июл 2016, 15:01

Подключение к интернету у каждого менеджера скачивания своё - независимое, или все-таки общая часть есть? И у подгрузки тайлов из интернета - то же общее, или своё?
Насколько геморройно прикрутить роутероподобный список из нескольких подключений, каждое со своими настройками прокси или хотя-бы просто список прокси, и при создании задания скачки предлагать выбирать одно из подключений из списка или один из прокси?
Аватара пользователя
DJ VK
Гуру
 
Сообщения: 1467
Зарегистрирован: 16 апр 2009, 13:57
Откуда: 8 км. от МКАД
Благодарил (а): 82 раз.
Поблагодарили: 298 раз.

Re: Вопросы по исходному коду

Сообщение DJ VK » 06 дек 2016, 22:25

Имеется интересный сервис, который парсит административно-территориальное деление из OSM: https://osm.wno-edv-service.de/boundaries/
Он не может экспортировать в KML. Пробовал создавать через SHP - теряется иерархичность границ.
Зато есть экспорт в geoJSON, с иерархией и мультиязычностью, причем ну очень похожего на kml (примерно как BCB и Delphi).
Хотел заняться конвертером в kml.
И возник такой вопрос: Может ввод напрямую через плагин sas будет несложно реализовать? Существую ли плагины для импорта меток и полигонов и парочка примерчиков?
Аватара пользователя
DJ VK
Гуру
 
Сообщения: 1467
Зарегистрирован: 16 апр 2009, 13:57
Откуда: 8 км. от МКАД
Благодарил (а): 82 раз.
Поблагодарили: 298 раз.

Пред.След.

Вернуться в Раздел для разработчиков программы SAS.Планета

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3